(可能重复,但我只能通过加入[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。
相关:
答案 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版本,数据分布,当然还有足够大的表。几百行测试不会告诉你太多。