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

Posted

技术标签:

【中文标题】SQL 连接与 SQL 子查询(性能)?【英文标题】:SQL Joins Vs SQL Subqueries (Performance)? 【发布时间】:2011-04-20 20:20:44 【问题描述】:

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

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)

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

还有什么时候我应该更喜欢一个而不是另一个?

对不起,如果这太琐碎并且之前问过,但我对此感到困惑。另外,如果你们能建议我工具 我应该用来衡量两个查询的性能,那就太好了。非常感谢!

【问题讨论】:

@Lucero,这个问题被标记为 sql-server-2008,你提到的帖子被标记为 mysql。你可以推断答案是一样的。两个 RDBMS 的性能优化方式不同。 【参考方案1】:

我希望第一个查询更快,主要是因为你有一个等价和一个明确的 JOIN。根据我的经验,IN 是一个非常慢的运算符,因为 SQL 通常将其评估为一系列由“OR”(WHERE x=Y OR x=Z OR...)分隔的 WHERE 子句。

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

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

【讨论】:

我对这个答案有很大的怀疑,因为大多数 DBMS,尤其是 SQL Server 2008 及更高版本,将单个 ID 子查询(不相关,意思是:不引用多个外部查询列)转换为相对快速的半-加入。此外,正如之前在另一个答案中所指出的,第一个真正的联接将为 Dept 中匹配 ID 的每次出现返回一行 - 这对于唯一 ID 没有区别,但会在其他地方为您提供大量重复项。使用 DISTINCT 或 GROUP BY 对它们进行排序将是另一个沉重的性能负载。在 SQL Server Management Studio 中检查执行计划! IN 子句与 OR 等效,适用于参数/值列表,但不适用于子查询,子查询大多被视为连接。【参考方案2】:

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

Optimizing Subqueries Optimizing Subqueries with Semijoin Transformations Rewriting Subqueries as Joins

我有一个包含 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,好。

【讨论】:

有趣,您能解释一下添加 GROUP BY 是如何解决它的吗? 子查询生成的临时表变小了。因此,由于要签入的数据更少,因此执行速度更快。 我认为在第一个查询中,您在外部查询和子查询之间共享变量,因此对于主查询中的每一行,子查询都会执行,但在第二个查询中,子查询只执行一次,这样性能就会提高。 Sql server 与 MySql 和 ...Sql(NoSql 除外)在基础设施上是如此相似。我们有一种查询优化引擎,它可以将 IN (...) 子句转换为 join(如果可能的话)。但是,当您在索引良好的列(基于其基数)上有一个 Group by 时,它会快得多。所以这真的取决于情况。 你确定缓冲区是干净的吗?如果您一个接一个地运行这两个查询,那么性能会有很大差异,这很有意义【参考方案3】:

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

如果是 20k 左右的数据较少。 JOIN 效果更好。

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

如果您不需要其他表中的数据,IN 很好,但使用 EXISTS 总是更好。

我测试的所有这些标准和表格都有适当的索引。

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

【参考方案5】:

性能应该是一样的;在您的表上应用正确的索引和集群更为重要(该主题存在some good resources)。

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

【参考方案7】:

我知道这是一篇旧文章,但我认为这是一个非常重要的话题,尤其是现在我们有 1000 万多条记录并谈论 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 驱动器能够以 1GB/秒的速度进行 i/o(这是字节而不是位),因此我的索引也非常快。所以在你的情况下,这也可能会影响速度

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

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

【讨论】:

【参考方案8】:

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

对于您的问题,an Exists filter 可能执行得最快。

【讨论】:

“Exists 过滤器的执行速度可能最快”——我认为可能不会,尽管明确的答案需要针对实际数据进行测试。如果存在具有相同查找值的多行,Exists 过滤器可能会更快 - 因此,如果查询正在检查其他员工是否已从同一部门记录,则存在过滤器可能会运行得更快,但在查找部门时可能不会表。 在最后一种情况下它会运行得更慢吗? 这取决于优化器 - 在某些情况下,它可能会,但通常我会期望非常相似的性能。

以上是关于SQL 连接与 SQL 子查询(性能)?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 子查询会导致性能损失吗?

013.子查询和分页子查询(sql实例)

优化where子句中的SQL子查询

SQL 内连接与子查询

sql子查询和连接查询的区别是啥呢?

sql MS SQL内部连接与子查询与CTE