ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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 的性能优化方式不同。

J
JNK

我希望第一个查询更快,主要是因为你有一个等价和一个明确的 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 等效,适用于参数/值列表,但不适用于子查询,子查询大多被视为连接。
M
Manuel Jordan

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

优化子查询

使用半连接转换优化子查询

将子查询重写为连接

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


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

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

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

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

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

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


H
HLGEM

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

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

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


L
Lucero

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

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


V
Vinnie Amir

我知道这是一篇旧文章,但我认为这是一个非常重要的话题,尤其是现在我们有 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) 并查看在进行大查询之前需要多长时间并乘以记录数。

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


o
onedaywhen

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

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


S
Snekse

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

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


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