1

(可能是重复的,但我只能通过 JOIN 找到问题和解决方案,[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 在 中存在 N 次acl,它将返回该行 0 - N 次而不是 0 - 1。

还有第四种方式:EXISTS. 感谢超立方体。

有关的:

4

1 回答 1

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 版本、您的数据分布,当然还有足够大的表。用几百行测试不会告诉你太多。

于 2013-03-20T22:26:41.403 回答