SQL 内连接与子查询

Posted

技术标签:

【中文标题】SQL 内连接与子查询【英文标题】:SQL inner join vs subquery 【发布时间】:2013-04-26 17:51:33 【问题描述】:

我正在处理以下查询:

Query 1: SELECT * From TabA INNER JOIN TabB on TabA.Id=TabB.Id
Query 2: SELECT * From TabA WHERE Id in (SELECT Id FROM TabB)
Query 3: SELECT TabA.* From TabA INNER JOIN TabB on TabA.Id=TabB.Id

我使用 SQL Server Profiler 调查了这些查询并发现了一些有趣的事实。

查询 1 耗时 2.312 秒 查询 2 耗时 0.811 秒 查询 3 耗时 0.944 秒

TabA 48716 行

TabB 62719 行

基本上我要问的是为什么查询 1 需要很长时间,而不是查询 3。我已经知道“子查询”比内部连接慢,但这里查询 2 最快;为什么?

【问题讨论】:

为什么会有C#标签?这无关紧要。 您是否多次运行这些计时?查询性能受表是否加载到页面缓存中的影响很大。 小心缓存。每次查询后你都会清理它吗? 查询 1 比查询 3 慢,因为您没有从查询 3 中的 TabB 加载数据 假设通过网络传输数据需要读取,TabB 可能包含大量列,其中有几个设置为大数据类型。是这样吗?只有查询 A 要求所有列和值都包含在结果集中并通过网络传输。以秒为单位的性能时间代表什么?客户往返总时间? 【参考方案1】:

如果我不得不猜测,我会说这是因为查询 1 正在从两个表中提取数据。查询 2 和 3(大约同时)仅提取 TabA 的数据。

您可以通过运行以下命令来检查这一点:

SET STATISTICS TIME ON
SET STATISTICS IO ON

我跑的时候

SELECT * FROM sys.objects

我看到了以下结果。

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 104 ms.

(242 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'sysschobjs'. Scan count 1, logical reads 10, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'syssingleobjrefs'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'syspalnames'. Scan count 1, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 866 ms.

您可以查看每个查询的扫描次数、逻辑读取次数和物理读取次数。物理读取当然需要更长的时间,并且代表从磁盘读取到缓存。如果您的所有读取都是逻辑读取,那么您的表完全在缓存中。

我敢打赌,如果你仔细观察,你会在查询 1 上看到比在查询 2 和 3 上更多的逻辑读取。

编辑:

出于好奇,我做了一些测试并将结果发布到博客here。

【讨论】:

【参考方案2】:

查询 1: 此查询从 TabA 和 TabB 中的所有行返回行,因此两个表的覆盖索引需要包含每个表中的所有行。要确切了解发生了什么,您需要查看查询计划。

查询 2 和查询 3: 您将从 TabA 返回所有行,并且只需要 TabB 的 Id 列的索引。我猜这里的差异与表统计信息有关,但是(再一次)我们必须查看查询计划才能确切知道发生了什么。

【讨论】:

【参考方案3】:

这仅仅是因为 SQL 不必执行 JOIN。您只是执行了两个查询,其中只有一个有 WHERE 子句。

我必须承认我没想到会有这么大的不同。

【讨论】:

不错!但是子查询应该比内部连接查询慢。 Reference 教科书上是这么说的,是的,但更有意义的是,幕后发生的事情越少,你得到结果的速度就越快。我想知道如果您从 JOIN 查询创建一个视图并对其进行性能测试会发生什么。顺便说一句,视图或存储过程比常规的 JOIN 查询要快得多,因为执行计划是存储的。【参考方案4】:

如果联接是一对多的,则可能需要时间的是重复数据。 相反,您可以将一组相关行格式化为 JSON 数组。 检查https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/10/09/returning-child-rows-formatted-as-json-in-sql-server-queries/上的“用例1”

【讨论】:

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

SQL 查询时间复杂度 - 连接与子查询

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

MSSQL之五 连接查询与子查询

选择中的左连接与子查询的奇怪问题

如何将查询与子查询连接起来?

与子查询相比,为啥左外连接查询给出不同的结果?