Mysql 联接表上的条件比引用上的条件快

Mysql 联接表上的条件比引用上的条件快,mysql,sql,join,Mysql,Sql,Join,我有一个涉及两个表的查询:表a有很多行,并且包含一个名为b_id的字段,该字段引用了表b中的一条记录,该表大约有30行不同的行。表A在b\u id上有一个索引,表b在name列上有一个索引 我的查询如下所示: SELECT COUNT(A.id) FROM A INNER JOIN B ON B.id = A.b_id WHERE (B.name != 'dummy') AND <condition>; 令我惊讶的是,这个查询实际上比直接连接慢,以北一秒 关于为什么联接比简化查询更

我有一个涉及两个表的查询:表
a
有很多行,并且包含一个名为
b_id
的字段,该字段引用了表
b
中的一条记录,该表大约有30行不同的行。表
A
b\u id
上有一个索引,表
b
name
列上有一个索引

我的查询如下所示:

SELECT COUNT(A.id) FROM A INNER JOIN B ON B.id = A.b_id WHERE (B.name != 'dummy') AND <condition>;
令我惊讶的是,这个查询实际上比直接连接慢,以北一秒

关于为什么联接比简化查询更快,有什么想法吗

更新:根据评论中的请求,解释的输出:

直联:

+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
| id | select_type | table | type   | possible_keys   | key     | key_len | ref           | rows   | Extra       |
+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
|  1 | SIMPLE      | A     | ALL    | b_id            | NULL    | NULL    | NULL          | 200707 | Using where |
|  1 | SIMPLE      | B     | eq_ref | PRIMARY,id_name | PRIMARY | 4       | schema.A.b_id |     1  | Using where |
+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
无连接:

+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | A     | ALL  | b_id          | NULL | NULL    | NULL | 200707 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
更新2: 尝试了另一种变体:

从A中选择COUNT(A.id),其中b_id位于()和


这比“无连接”运行得更快,但仍然比“连接”运行得慢,因此,似乎不等式操作是造成性能损失的部分原因,但不是全部原因。

这可能是一个注释,而不是答案,但会有点长

首先,很难相信两个解释(几乎)完全相同的查询以不同的速度运行。此外,如果解释中有额外行的那一行运行得更快,那么这种情况就不太可能发生。我猜“更快”这个词是这里的关键

您已经比较了速度(查询完成所需的时间),这是一种非常经验性的测试方法。例如,您可能不正确地禁用了缓存,这使得该比较毫无用处。更不用说您的
可能在运行测试时出现页面错误或任何其他操作,从而导致查询速度降低

衡量查询性能的正确方法是基于解释(这就是为什么它存在的原因)

因此,我必须回答的最接近的问题是:关于为什么连接会比简化查询更快,有什么想法吗?。。。简而言之,这是第8层错误

不过,我还有其他一些意见,这些意见应该考虑在内,以便加快速度。如果
A.id
是主键(名称闻起来像主键),根据您的解释,为什么
count(A.id)
必须扫描所有行?它应该能够直接从索引中获取数据,但我在额外的标志中没有看到使用索引的
。看起来您甚至没有唯一的索引,而且它不是一个不可为空的字段。这闻起来也很奇怪。确保该字段不为空,并且该字段上有唯一的索引,再次运行解释,确认额外的标志包含使用索引的
,然后(正确地)对查询计时。它应该跑得更快

还要注意的是,一种与我上面提到的相同的性能改进方法是将
count(A.id)
替换为
count(*)


只有我的2美分。

因为MySQL不会对
索引使用索引=val
在何处

优化器将通过猜测决定使用索引。由于“!=”更有可能获取所有内容,它跳过并阻止使用索引来减少开销。(是的,mysql很愚蠢,而且它不统计索引列)

通过在(除val之外的所有内容)
中使用
索引,您可以更快地选择MySQL将学习使用索引


如果您使用的是MySQL 5.6或更高版本,那么您可以询问查询优化器它在做什么

SET optimizer_trace="enabled=on";

## YOUR QUERY 
SELECT COUNT(*) FROM transactions WHERE (id < 9000) and user != 11;
##END YOUR QUERY

SELECT trace FROM information_schema.optimizer_trace;

SET optimizer_trace="enabled=off";
因此,与查询一样,这一切都取决于索引——或者更准确地说,是缺少索引。仅仅因为您在单个字段上有索引,并不一定意味着这些字段适合您正在运行的查询

基本规则:如果
EXPLAIN
没有说使用索引,那么您需要添加一个合适的索引

看看解释输出,讽刺的是,第一件有趣的事情是每行的最后一件事;即
Extra

在第一个例子中,我们看到

|  1 | SIMPLE      | A     | .... Using where |
|  1 | SIMPLE      | B     | ...  Using where |
这两个都使用where不好;理想情况下,至少有一个,最好两个都应该使用索引说

当你这样做的时候

SELECT COUNT(A.id) FROM A WHERE (b_id != 23) AND <condition>;
您应该看到使用where;使用索引(这里假设Id是主键并且有索引)

如果随后在末尾添加了条件

EXPLAIN SELECT COUNT(A.id) FROM A WHERE (Id > 23) and Field > 0
请参见使用where,然后需要为这两个字段添加索引。仅仅在一个字段上有一个索引并不意味着MySQL能够在跨多个字段的查询期间使用该索引——这是查询优化器将在内部决定的事情。我不完全确定内部规则;但一般来说,添加一个额外的索引来匹配查询非常有帮助

因此,添加索引(在上面查询中的两个字段上):

应该更改它,以便在基于这两个字段进行查询时有一个索引

我已经在我的一个数据库上尝试过这个方法,该数据库包含
事务
用户

我将使用此查询

EXPLAIN SELECT COUNT(*) FROM transactions WHERE (id < 9000) and user != 11;
然后添加一个索引:

ALTER TABLE `transactions` ADD INDEX `IndexIdUser` (`id`, `user`);
然后再次执行相同的查询,这一次

PRIMARY,user,Index 4    Index 4 4   NULL    12628   Using where; Using index
这一次它使用的是索引,因此会更快


从@Wrikken的评论中,还要记住,我没有准确的模式/数据,因此一些调查需要对模式进行假设(这可能是错误的)

如果我们看一下OP中的第一个解释,我们会发现查询有两个元素。参考*eq_ref*的文档,我可以看到这将基于此关系定义要考虑的行

解释输出的顺序并不一定意味着它先做一个,然后再做另一个;它只是选择了什么来执行查询(至少据我所知)

出于某种原因,查询优化器决定不在
buid
上使用索引-我在这里假设
ALTER TABLE `A` ADD INDEX `IndexIdField` (`Id`,`Field`)
EXPLAIN SELECT COUNT(*) FROM transactions WHERE (id < 9000) and user != 11;
PRIMARY,user    PRIMARY 4   NULL    14334   Using where
ALTER TABLE `transactions` ADD INDEX `IndexIdUser` (`id`, `user`);
PRIMARY,user,Index 4    Index 4 4   NULL    12628   Using where; Using index
SELECT COUNT(A.id) FROM A FORCE INDEX (b_id)

would perform at least as good as 

SELECT COUNT(A.id) FROM A INNER JOIN B ON A.b_id = B.id.
  SELECT COUNT(A.id) FROM A INNER JOIN B ON A.b_id = B.id.
SELECT COUNT(A.id)
FROM A
WHERE IF(b_id!=23, <condition>, 0);