SQL 效率:WHERE IN 子查询与 JOIN 然后 GROUP

Posted

技术标签:

【中文标题】SQL 效率:WHERE IN 子查询与 JOIN 然后 GROUP【英文标题】:SQL Efficiency: WHERE IN Subquery vs. JOIN then GROUP 【发布时间】:2010-11-13 20:01:26 【问题描述】:

例如,我想获取应用了特定标签的所有项目的列表。我可以执行以下任一操作:

SELECT Item.ID, Item.Name
FROM Item
WHERE Item.ID IN (
    SELECT ItemTag.ItemID
    FROM ItemTag
    WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55)

或者

SELECT Item.ID, Item.Name
FROM Item
LEFT JOIN ItemTag ON ItemTag.ItemID = Item.ID
WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55
GROUP BY Item.ID, Item.Name

或者完全不同的东西。

一般来说(假设有一个通用规则),什么是更有效的方法?

【问题讨论】:

@Larsenal:您可以在第二个查询中将LEFT JOIN 替换为INNER JOIN,结果将是相同的。对于ItemTag 中没有对应Item.ID 的行,LEFT JOIN 将返回NULLs,而您的WHERE 条件会将它们过滤掉。 【参考方案1】:
SELECT Item.ID, Item.Name
FROM Item
WHERE Item.ID IN (
    SELECT ItemTag.ItemID
    FROM ItemTag
    WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55)

SELECT Item.ID, Item.Name
FROM Item
LEFT JOIN ItemTag ON ItemTag.ItemID = Item.ID
WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55
GROUP BY Item.ID

您的第二个查询无法编译,因为它引用了 Item.Name 而不对其进行分组或聚合。

如果我们从查询中删除GROUP BY

SELECT  Item.ID, Item.Name
FROM    Item
JOIN    ItemTag
ON      ItemTag.ItemID = Item.ID
WHERE   ItemTag.TagID = 57 OR ItemTag.TagID = 55

这些仍然是不同的查询,除非 ItemTag.ItemIdUNIQUE 键并标记为这样。

SQL Server 能够检测到UNIQUE 列上的IN 条件,并且只会将IN 条件转换为JOIN

如果ItemTag.ItemID不是UNIQUE,第一个查询将使用一种SEMI JOIN算法,在SQL Server中效率很高。

您可以将第二个查询转换为JOIN

SELECT  Item.ID, Item.Name
FROM    Item
JOIN    (
        SELECT DISTINCT ItemID
        FROMT  ItemTag
        WHERE  ItemTag.TagID = 57 OR ItemTag.TagID = 55
        ) tags
ON      tags.ItemID = Item.ID

但是这个效率比INEXISTS 低一点。

有关更详细的性能比较,请参阅我的博客中的这篇文章:

IN vs. JOIN vs. EXISTS

【讨论】:

【参考方案2】:

我认为这将取决于优化器如何处理它们,甚至可能最终获得相同的性能。在这里显示执行计划是你的朋友。

【讨论】:

【参考方案3】:
SELECT Item.ID, Item.Name
...
GROUP BY Item.ID

这不是有效的 T-SQL。 Item.Name 必须出现在 group by 子句或聚合函数中,例如 SUM 或 MAX。

【讨论】:

【参考方案4】:

如果不查看执行计划和/或运行一些压力测试,几乎不可能(除非您是那些疯狂的 DBA 大师之一)来判断什么会很快,什么不会。

【讨论】:

其实很容易说:第二个要快得多。它只会拒绝在一纳秒左右的时间内编译。 @Quassnoi 这不会让它变慢吗?返回结果需要无限的时间......【参考方案5】:

运行这个:

SET SHOWPLAN_ALL ON

然后运行每个版本的查询

您可以查看它们是否返回相同的计划,如果不是,请查看每个计划第一行的 TotalSubtreeCost 并了解它们的不同之处。

【讨论】:

【参考方案6】:

性能似乎总能赢得选票,但你也听说“买硬件比程序员便宜”

第二个在性能上获胜。

有时查看 SQL 并了解其用途是件好事,但这就是 cmets 的用途。第一个查询使用另一个表作为过滤器 - 非常简单。

第二个使用 distinct 而不是 group by 会更有意义(从理解目的而不是性能)。我希望一些聚合会在选择中,但没有。速度杀人。

【讨论】:

【参考方案7】:

第二个在 mysql 中效率更高。对于每个 WHERE 条件测试,MySQL 都会在 IN 语句中重新执行查询。

【讨论】:

以上是关于SQL 效率:WHERE IN 子查询与 JOIN 然后 GROUP的主要内容,如果未能解决你的问题,请参考以下文章

为啥 MySQL JOIN 比 WHERE IN (子查询) 快得多

sql中where的与jnner join on的连接条件.哪个优先级别高?

MySql子查询(wherefromexists)及连接查询(left joinright joininner joinunion join)

sql中in和exist语句的区别?(补充了left join和right join)

在sql语句多表连接中,in、exists、join哪个效率更高一点?

MS-Access:SQL JOIN 和 INSERT INTO 与 WHERE 慢