我想选择一个表中的行,其中主键位于另一个表中。我不确定是否应该在 SQL Server 2005 中使用 JOIN 或 IN 运算符。这两个具有大型数据集的 SQL 查询之间是否存在显着的性能差异(即数百万行)?

SELECT *
FROM a
WHERE a.c IN (SELECT d FROM b)

SELECT a.*
FROM a JOIN b ON a.c = b.d
有帮助吗?

解决方案

更新:

我博客中的这篇文章总结了我的答案和我对其他答案的评论,并显示了实际的执行计划:


SELECT  *
FROM    a
WHERE   a.c IN (SELECT d FROM b)

SELECT  a.*
FROM    a
JOIN    b
ON      a.c = b.d

这些查询并不等同。如果您的表,它们可能会产生不同的结果 b 未保留密钥(i.e.的价值观 b.d 并不唯一)。

第一个查询的等效内容如下:

SELECT  a.*
FROM    a
JOIN    (
        SELECT  DISTINCT d
        FROM    b
        ) bo
ON      a.c = bo.d

如果 b.dUNIQUE 并标记为这样(带有 UNIQUE INDEX 或者 UNIQUE CONSTRAINT),那么这些查询是相同的,并且很可能会使用相同的计划,因为 SQL Server 足够聪明,能够考虑到这一点。

SQL Server 可以采用以下方法之一来运行此查询:

  • 如果有一个索引 a.c, dUNIQUEb 相对较小 a, ,然后条件被传播到子查询和普通查询中 INNER JOIN 使用(与 b 领导)

  • 如果有一个索引 b.dd 不是 UNIQUE, ,那么条件也被传播并且 LEFT SEMI JOIN 用来。也可用于上述情况。

  • 如果两者都有索引 b.da.c 而且它们很大,那么 MERGE SEMI JOIN 用来

  • 如果任何表上都没有索引,则建立哈希表 bHASH SEMI JOIN 用来。

两者都不 这些方法每次都会重新评估整个子查询。

有关其工作原理的更多详细信息,请参阅我的博客中的此条目:

都有链接供大家参考 RDBMS属于四大的。

其他提示

都不是。使用ANSI-92 JOIN:

SELECT a.*
FROM a JOIN b a.c = b.d

然而,最好是作为EXISTS

SELECT a.*
FROM a
WHERE EXISTS (SELECT * FROM b WHERE a.c = b.d)

这删除了可能由JOIN生成的重复项,但如果不是更快则运行速度

对a中的每一行评估IN(以及从b重新运行中选择),而JOIN优化为使用索引和其他整齐的分页技巧......

但在大多数情况下,优化器很可能能够从相关子查询中构造JOIN,并最终得到相同的执行计划。

编辑:请阅读下面的评论,以便进一步讨论这个答案的有效性,以及OP问题的实际答案。 =)

根据49,000,000行的表格经验,我建议LEFT OUTER JOIN。 使用IN或EXISTS花费5分钟完成LEFT OUTER JOIN在1秒内完成的位置。

SELECT a.*
FROM a LEFT OUTER JOIN b ON a.c = b.d
WHERE b.d is not null -- Given b.d is a primary Key with index

实际上,在我的查询中,我在9个表中执行此操作。

除了自己测试数据之外,我会说使用JOINS。在大多数情况下,与IN子查询相比,我总是使用它们更好的表现,并且你有更多的自定义选项,如何加入,选择什么,什么不是等等。

它们是具有不同结果的不同查询。使用IN查询,只要谓词匹配,您将从表'a'获得1行。使用INNER JOIN查询,只要连接条件匹配,您将获得* b行。 因此,对于{1,2,3}中的{1,2,3}和b的值,您将从JOIN获得1,2,2,3,从IN获得1,2,3。

编辑 - 我想你可能会在这里遇到一些答案会给你一个误解。自己测试一下,你会发现这些都是很好的查询计划:

create table t1 (t1id int primary key clustered)
create table t2 (t2id int identity primary key clustered
    ,t1id int references t1(t1id)
)


insert t1 values (1)
insert t1 values (2)
insert t1 values (3)
insert t1 values (4)
insert t1 values (5)

insert t2 values (1)
insert t2 values (2)
insert t2 values (2)
insert t2 values (3)
insert t2 values (4)


select * from t1 where t1id in (select t1id from t2)
select * from t1 where exists (select 1 from t2 where t2.t1id = t1.t1id)
select t1.* from t1 join t2 on t1.t1id = t2.t1id

前两个计划完全相同。最后一个计划是嵌套循环,这种差异是预期的,因为正如我上面提到的,连接具有不同的语义。

从子查询基础知识的 MSDN文档

  

许多Transact-SQL语句   包括子查询可以   或者配制成连接。   其他问题只能提出   子查询。在Transact-SQL中,有   通常没有性能差异   在包含a的语句之间   子查询和语义等价   没有的版本。但是,在   存在必须存在的一些情况   检查,连接产生更好   性能。否则,嵌套   必须为每个查询处理查询   外部查询的结果,以确保   消除重复。在这样的   案例,联合方法会产生   更好的结果。

在您提供的示例中,嵌套查询只需要为每个外部查询结果处理一次,因此应该没有性能差异。检查两个查询的执行计划应该确认这一点。

注意:虽然问题本身没有指定SQL Server 2005,但我根据问题标签回答了这个假设。其他数据库引擎(甚至不同的SQL Server版本)可能无法以相同的方式进行优化。

观察两种类型的执行计划并得出结论。除非子查询在“IN”中返回的记录数量。声明很小,IN变体几乎肯定会慢一些。

我会使用一个加入,打赌它会比IN快得多。这假定当前定义了主键,因此索引可以极大地提高速度。

通常认为连接比IN子查询更有效;但是SQL * Server优化器通常不会导致明显的性能差异。即便如此,最好使用连接条件进行编码以保持标准的一致性。此外,如果将来需要迁移您的数据和代码,数据库引擎可能不会那么宽容(例如,使用连接而不是IN子查询会在MySql中产生巨大的差异)。

理论只会让你到目前为止这样的问题。在一天结束时,您将要测试两个查询并查看实际运行速度更快的查询。我曾经遇到JOIN版本花了一分钟而IN版本花了不到一秒的情况。我也有过JOIN实际上更快的情况。

就个人而言,如果我知道我不需要子查询表中的任何字段,我倾向于从IN版本开始。如果开始运行缓慢,我会优化。幸运的是,对于大型数据集,重写查询会产生如此显着的差异,您只需从查询分析器中获取时间并知道您正在取得进展。

祝你好运!

我一直是IN方法的支持者。此链接包含在PostgresSQL中进行的测试的详细信息。 http://archives.postgresql.org/pgsql-performance/2005- 02 / msg00327.php

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top