IN与大型行集的JOIN

时间:2009-06-16 13:39:20

标签: sql sql-server-2005 performance join

我想在表中选择主键位于另一个表中的行。我不确定我是否应该在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

12 个答案:

答案 0 :(得分:29)

<强>更新

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


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

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

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

第一个查询的等价物如下:

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

如果b.dUNIQUE且标记为UNIQUE INDEXUNIQUE CONSTRAINT,那么这些查询是相同的,并且很可能会使用相同的计划,因为{ {1}}足够聪明,可以考虑到这一点。

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

  • 如果SQL Server上有索引,a.cdUNIQUEb相比相对较小,则条件为传播到子查询中并使用普通aINNER JOIN领先)

  • 如果b上的索引和b.d不是d,则条件也会传播,并使用UNIQUE。它也可用于上述情况。

  • 如果LEFT SEMI JOINb.d都有索引并且它们很大,则会使用a.c

  • 如果任何表上没有索引,则会在MERGE SEMI JOIN上构建哈希表,并使用b

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

请参阅我的博客中的此条目,详细了解其工作原理:

四大国家的所有HASH SEMI JOIN都有链接。

答案 1 :(得分:5)

都不是。使用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生成的重复项,但如果不是更快则运行速度

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:4)

根据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个表中执行此操作。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

它们是具有不同结果的不同查询。使用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

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

答案 6 :(得分:2)

来自子查询基础知识的MSDN documentation

  

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

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

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

答案 7 :(得分:1)

观察两种类型的执行计划并得出结论。除非子查询在“IN”语句中返回的记录数非常少,否则IN变量几乎肯定会更慢。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

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

祝你好运!

答案 11 :(得分:0)

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