连接查询和子查询哪个更快,为什么?我什么时候应该选择其中一种而不是另一种?

问题描述 投票:0回答:8

我有一个连接查询

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)

哪个会更快,为什么?

我什么时候应该选择其中一种而不是另一种?

sql performance sql-server-2008 subquery join
8个回答
59
投票

嗯,我相信这是一个“古老但黄金”的问题。答案是:“这要看情况!”。 性能是一个如此微妙的主题,以至于说“永远不要使用子查询,总是加入”就太愚蠢了。 在以下链接中,您将找到一些我认为非常有帮助的基本最佳实践:

我有一个包含 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.0256s

好 SQL,好。


58
投票

我希望第一个查询会更快,主要是因为您有一个等价项和一个显式 JOIN。根据我的经验,

IN
是一个非常慢的运算符,因为 SQL 通常将其评估为一系列由“OR”分隔的
WHERE
子句 (
WHERE x=Y OR x=Z OR...
)。

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

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


18
投票

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

如果数据较少的话20k左右。加入效果更好。

如果数据更像 100k+,那么 IN 效果更好。

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

我测试了所有这些标准,并且表格具有正确的索引。


11
投票

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

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

EXISTS 通常比这两者中的任何一个都更好,当您谈论左连接时,您想要不在左连接表中的所有记录,那么 NOT EXISTS 通常是更好的选择。


6
投票

性能应该是一样的;在表上应用正确的索引和集群更为重要(关于该主题存在一些好的资源)。

(编辑以反映更新的问题)


6
投票

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

我还将强调以下观察结果。我的表 ([data]) 中有大约 45M 条记录,[cats] 表中有大约 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 分钟。

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

如果是一次性数据清理,最好让它运行并完成。我使用 TOP(10000) 并查看在执行大查询之前需要多长时间并乘以记录数。

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


5
投票

这两个查询在语义上可能不相同。如果一名员工为多个部门工作(可能在我工作的企业中;不可否认,这意味着您的表未完全规范化),那么第一个查询将返回重复的行,而第二个查询则不会。为了使这种情况下的查询等效,必须将

DISTINCT
关键字添加到
SELECT
子句中,这可能会对性能产生影响。

请注意,有一个设计经验法则,规定表应该对实体/类或实体/类之间的关系进行建模,但不能同时对两者进行建模。因此,我建议您创建第三个表,例如

OrgChart
,来模拟员工和部门之间的关系。


1
投票

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

对于您的问题,“存在”过滤器可能执行速度最快。

© www.soinside.com 2019 - 2024. All rights reserved.