哪个子查询更快?

时间:2013-03-20 22:16:34

标签: mysql sql subquery

(可能重复,但我只能通过加入[3]找到问题和解决方案,而不是选项。)

我有两张桌子。非常薄(很少列)和很长(很多行)。一个是数据表(articles),一个是ACL表(acl)。

我想只显示我可以通过acl.some_id访问的文章。哪个子查询更快?

[1]
SELECT a.title
FROM articles a
WHERE 0 < (
  SELECT COUNT(1)
  FROM acl
  WHERE article_id = a.id AND some_id IN (1, 2, 3)
)

[2]
SELECT a.title
FROM articles a
WHERE a.id IN (
  SELECT article_id
  FROM acl WHERE some_id IN (1, 2, 3)
)

我的想法会说第二个,因为子查询可以重用于所有可能匹配的行,所以只执行一次(虽然结果集会非常大),而第一个子查询必须检查对于每个可能匹配的行。

还有第三种方法,但是不是选项,因为它会重复行(并且GROUP BY不是解决方案,因为我以后需要COUNT用于其他东西(并且DISTINCT永远不是解决方案) !)):

[3]
SELECT a.title
FROM articles a
JOIN acl
  ON acl.article_id = a.id
WHERE acl.some_id IN (1, 2, 3)

由于article_id X在acl中存在N次,因此它将返回该行0 - N次而不是0 - 1。

还有第四种方式:EXISTS。感谢ypercube。

相关:

1 个答案:

答案 0 :(得分:5)

我也会说[2],但MySQL在优化IN子查询方面有一些盲点,至少高达5.5。在(新发布的)5.6版本中,查询优化器有一些改进。您可以在MySQL文档中阅读(半连接和IN子查询): MySQL 5.6: Optimizing Subqueries with Semi-Join Transformations

MariaDB(版本5.3和5.5)中的优化器也有一些改进,有些与这种查询有关。您可以在他们的文档中阅读: MariaDB 5.3: Semi-join subquery optimizations

您还可以尝试使用EXISTS版本,尤其是在使用5.5或更早版本时:

-- [4]
SELECT id
FROM articles AS a
WHERE EXISTS (
  SELECT *
  FROM acl 
  WHERE acl.some_id IN (1, 2, 3)
    AND acl.article_id = a.id 
) ;

我认为(article_id, some_id)上的索引在这里很有用 - 或者反过来一个,尝试两者都没有坏处。


如果您可以信任来自acl (article_id) REFERENCES article (id)的外键,并且您只需要文章ID,您也可以只从一个表中获取数据:

SELECT DISTINCT article_id
FROM acl 
WHERE acl.some_id IN (1, 2, 3) ;

当然,您应该测试服务器中的几个版本,您拥有(或计划使用)的MySQL版本,数据分布,当然还有足够大的表。几百行测试不会告诉你太多。