我在优化这个查询时遇到了困难:
SELECT a.id
FROM a
JOIN b ON a.id=b.id
LEFT JOIN c ON a.id=c.id
WHERE
(b.c1='12345' OR c.c1='12345')
AND (a.c2=0 OR b.c3=1)
AND a.c4='active'
GROUP BY a.id;
当只有b
或c
其中一个被连接时,查询只需0秒,但是当两者同时被连接时,查询需要7秒。以下是详细解释:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: PRIMARY(id),c4,c2
key: c4
key_len: 1
ref: const
rows: 80775
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ref
possible_keys: id_c1_unique,id
key: id_c1
key_len: 4
ref: database.a.id
rows: 1
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ref
possible_keys: id_c1_unique,id,c1,c3
key: id
key_len: 4
ref: database.a.id
rows: 2
Extra: Using where
从表b中总是恰好匹配1行,而从表c中最多只匹配1行。如果MySQL先获取与字面值匹配的b和c行,然后基于id
连接,速度会更快,但它却从开始。
细节:
- MyISAM
- 所有列都有索引(_unique为唯一索引)
- 所有列都不为空
我尝试过:
- 更改JOIN的顺序
- 将WHERE条件移动到ON子句中
- 对b.c1和c.c1使用子查询(WHERE b.id = (SELECT b.id FROM b WHERE c1 = '12345'))
- 对b和c使用USE INDEX
我知道可以使用两个带UNION的SELECT来完成此操作,但如果可能的话,我需要避免这样做,因为查询是如何生成的。
编辑:添加CREATE TABLEs
使用相关列创建的CREATE TABLEs
。
CREATE TABLE `a` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`c2` tinyint(1) NOT NULL,
`c4` enum('active','pending','closed') NOT NULL,
PRIMARY KEY (`id`),
KEY `c2` (`c2`)
KEY `c4` (`c4`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `b` (
`b_id` int(11) NOT NULL AUTO_INCREMENT,
`id` int(11) NOT NULL DEFAULT '0',
`c1` int(11) NOT NULL,
`c3` tinyint(1) NOT NULL,
PRIMARY KEY (`b_id`),
UNIQUE KEY `id_c1_unique` (`id`,`c1`),
KEY `c1` (`c1`),
KEY `c3` (`c3`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `c` (
`c_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`id` int(11) NOT NULL,
`c1` int(11) NOT NULL,
PRIMARY KEY (`c_id`),
UNIQUE KEY `id_c1_unique` (`id`,`c1`),
KEY `id` (`id`),
KEY `c1` (`c1`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
a
的结构吗?a.id
是自增字段吗? - NemodenGROUP BY
。如果这个表既用于读取又用于更新,你会遇到这样的性能问题。我希望这个表是InnoDB
?这很糟糕:Using where; Using temporary; Using filesort
。我会在一个临时表上执行这个查询,它将是SELECT * FROM a WHERE c4 = 'active'
。我还会把c4
转换为int
,这样1
就表示active
状态。 - Nemoden