SQL连接与SQL子查询(性能)?


110

我想知道我是否有类似这样的联接查询-

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

和一个类似这样的子查询 -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

当我考虑性能时,两个查询中的哪个查询会更快,为什么

还有一段时间我应该优先选择另一个吗?

抱歉,这太琐碎了,之前问过,但是我对此感到困惑。另外,如果你们能建议我一些我可以用来衡量两个查询性能的工具,那将是很棒的。非常感谢!


5
@Lucero,此问题标记为sql-server-2008,其中您提到的帖子标记为MySql。您可以推断出答案将是相同的。在两个RDBMS上执行性能优化的方式有所不同。
弗朗索瓦·博塔

Answers:


48

我希望第一个查询更快,主要是因为您有一个等效项和一个显式的JOIN。以我的经验,IN运算符非常慢,因为SQL通常将其评估为一系列WHERE用“ OR”(WHERE x=Y OR x=Z OR...)分隔的子句。

与ALL THINGS SQL一样,您的里程可能会有所不同。速度很大程度上取决于索引(您是否在两个ID列上都有索引?这将有很大帮助...)。

唯一能百分百确定哪个更快的真实方法是打开性能跟踪(IO Statistics特别有用)并同时运行它们。确保在两次运行之间清除缓存!


16
我对这个答案有严重的疑问,因为大多数DBMS(肯定是SQL Server 2008和更高版本)都将单个ID子查询(不相关,即:不引用多个外部查询列)转换为相对快速的半联接。而且,如先前在另一个答案中所述,第一个真正的联接将为在Dept中出现匹配ID的每行返回一行-这对于唯一ID没有影响,但会在其他地方给您带来大量重复项。用DISTINCT或GROUP BY进行分类将是另一个沉重的性能负担。在SQL Server Management Studio中检查执行计划!
Erik Hart 2013年

2
与OR等效的IN子句适用于参数/值列表,但不适用于子查询,这些子查询通常被视为联接。
Erik Hart 2013年

42

好吧,我相信这是一个“古老而又黄金”的问题。答案是:“取决于!”。表演是如此精致,以至于说:“从不使用子查询,总是加入”,这太愚蠢了。在以下链接中,您会发现一些我发现非常有帮助的基本最佳实践:

我有一个包含50000个元素的表,我想要的结果是739个元素。

我最初的查询是这样的:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

执行时间为7.9秒。

我的查询最后是这样的:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

花了0.0256秒

好的SQL,好的。


3
有趣的是,您能解释一下如何添加固定的GROUP BY吗?
cozos

6
子查询生成的临时表较小。因此,由于要检查的数据较少,因此执行速度更快
。– Sirmyself

2
我认为在第一个查询中,外部查询和子查询之间具有共享变量,因此对于主查询中的每一行,都会执行子查询,而在第二个查询中,子查询仅执行一次,因此性能得以提高。
Ali Faradjpour

1
Sql服务器与MySql和... Sql(NoSql除外)在基础架构中是如此相似。我们下面有一种查询优化引擎,该引擎将IN(...)子句转换为联接(如果可能)。但是,当您在索引良好的列上使用分组依据(基于基数)时,它将更快。因此,这实际上取决于情况。
Alix

10

开始查看执行计划,以了解SQl Server将如何解释它们的差异。您还可以使用Profiler实际多次运行查询并获得差异。

我不希望它们有如此可怕的区别,当您使用关联子查询时,使用连接而不是子查询可以真正获得较大的性能提升。

EXISTS通常比这两个中的任何一个都要好,并且当您要在左连接中要所有记录不在左连接表中时,使用NOT EXISTS通常是更好的选择。


9

性能取决于您正在执行的数据量...

如果较少,则大约20k的数据。JOIN效果更好。

如果数据更像是100k +,那么IN工作得更好。

如果您不需要其他表中的数据,则IN不错,但是最好还是选择EXISTS。

我测试的所有这些条件和表均具有正确的索引。



4

这两个查询在语义上可能不相等。如果某个员工在多个部门工作(可能在我工作的企业中;诚然,这意味着您的表未完全规范化),则第一个查询将返回重复的行,而第二个查询则不会。为了在这种情况下使查询等效,必须将DISTINCT关键字添加到SELECT子句中,这可能会对性能产生影响。

请注意,有一条设计经验法则指出表应该对实体/类或实体/类之间的关系建模,但不能对两者都建模。因此,我建议您创建第三个表,例如OrgChart,以模拟员工和部门之间的关系。


4

我知道这是一篇过时的文章,但是我认为这是一个非常重要的话题,尤其是在当今我们拥有10M +记录并谈论TB级数据的情况下。

我还将强调以下几点。我的表([数据])中有大约4500万条记录,[猫]表中有大约300条记录。对于要讨论的所有查询,我都有大量的索引。

考虑示例1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

与示例2相比:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

示例1大约需要23分钟才能运行。实施例2花费了约5分钟。

因此,我可以得出结论,这种情况下的子查询要快得多。当然,请记住,我正在使用I.o @ 1GB / sec(即字节而不是位)的I.O的M.2 SSD驱动器,因此索引的速度也非常快。所以这也可能会影响您的速度

如果要一次性清理数据,则最好让它运行并完成。我使用TOP(10000)来查看花费多长时间并乘以记录数,然后再执行大查询。

如果您正在优化生产数据库,我强烈建议您进行预处理数据,即使用触发器或作业代理来异步更新记录,以便实时访问检索静态数据。


0

您可以使用解释计划获得客观答案。

对于您的问题,Exists过滤器的执行速度可能最快。


2
“一个存在的过滤器可能执行得最快”-我想,也许不是,尽管确定的答案需要对实际数据进行测试。在有多个具有相同查找值的行的情况下,现有过滤器可能会更快-因此,如果查询正在检查是否已记录同一部门的其他员工,则存在的过滤器可能会运行得更快,但在查询某个部门时可能不会更快表。

在最后一种情况下运行速度会变慢吗?
Snekse 2010年

这取决于优化器-在某些情况下,可能会,但是通常我希望性能非常相似。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.