3 sql-server execution-plan sql-server-2012
想象一下有一个包含这些字段的表:
Employee: E-Number
E-Name
Department
Salary
Run Code Online (Sandbox Code Playgroud)
我们需要编写一个查询来找到这个表中第二高的薪水。
我发现这个查询:
select distinct salary
from Employee e1
where 2 = ( select count(distinct salary)
from Employee e2
where e1.salary < e2.salary)
Run Code Online (Sandbox Code Playgroud)
我对这部分有点困惑,where 2 =
因为我以前从未见过这样的事情。
任何人都可以在这里解释更多关于查询和部分的信息where 2 =
吗?子查询的结果是什么,表行(e1,e2)以什么方式相互比较?
实际上这是执行计划,谁能解释更多步骤?
我将解决问题的这一部分:
...这是执行计划 谁能解释更多步骤?
假设表定义如下所示:
CREATE TABLE dbo.Employee
(
[E-Number] integer NOT NULL,
[E-Name] nvarchar(50) NOT NULL,
Department nvarchar(30) NOT NULL,
Salary smallmoney NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
以下查询(不是我的建议,只是一个例子):
SELECT DISTINCT
E1.Salary
FROM dbo.Employee AS E1
WHERE
1 =
(
SELECT
COUNT(DISTINCT E2.Salary)
FROM dbo.Employee AS E2
WHERE
E2.Salary > E1.Salary
);
Run Code Online (Sandbox Code Playgroud)
给出这个执行计划(表中没有数据):
这些数字对应于每个运营商的节点 ID 属性。
E1
表扫描(4)返回的所有行(没有特定的顺序)。对于每一行:
E1.Salary
值作为参数传递。E2
表扫描(8)发现的行匹配E2.Salary > E1.Salary
E2.Salary
。E2.Salary
)。新的计数列被赋予标签Expr1008
。bigint
,integer
因为查询指定COUNT
而不是COUNT_BIG
。新列的标签为Expr1004
。Expr1004 = 1
E1.Salary
值。如果我们向表中添加几行,并带有一些重复Salary
值:
INSERT dbo.Employee
([E-Number], [E-Name], Department, Salary)
VALUES
(1, 'A', 'D1', $1),
(2, 'B', 'D1', $1),
(3, 'C', 'D1', $2),
(4, 'D', 'D1', $2),
(5, 'E', 'D1', $3),
(6, 'F', 'D1', $3);
Run Code Online (Sandbox Code Playgroud)
上面的查询生成了一个包含几个新运算符的计划(突出显示):
新的 Sort 按 排序行E1.Salary
。确保任何重复的Salary
值按顺序提供给连接。
Lazy Index Spool 会在其下方的操作符到达时保存它们的输出。它们是 Stream Aggregate、Distinct Sort 和E2
Table Scan(带谓词),它们如前所述过滤E2
(使用 的当前值E1.Salary
)、删除重复E2.Salary
值并计算结果行。Lazy Index Spool 保存重新计算结果,以防Salary
表中的值相同E1
在嵌套循环连接的下一次迭代中出现。
因为 Sort 从E1
by排序的行E1.Salary
,任何重复项都会一个接一个地呈现给 join,所以可以重用 spool 中保存的结果,而不是E2
每次都重新运行 Stream Aggregate、Distinct Sort 和Table Scan。
线轴上的索引由 键控Salary
,因此可以快速找到COUNT(DISTINCT E2.Salary)
当前E1.Salary
值的保存结果。
如果查看计划中每个运算符的 Actual Executions 值,您将看到 Lazy Index Spool 执行 6 次(每个外部行一次),而 Stream Aggregate、Distinct Sort 和E2
Table Scan 仅执行 3 次(一次每个不同的工资值)。
优化器引入了额外的排序和假脱机作为性能优化,基于自动收集的有关表中数据的统计信息。
小智 6
好吧,首先,这段代码片段并不完全正确。对于第二高的薪水,应该是
where 1 = ( select count(distinct salary)
from Employee e2
where e1.salary < e2.salary)
Run Code Online (Sandbox Code Playgroud)
因为这个子查询基本上是计算高于当前值的薪水,如果你想要第二高的薪水,那么只能有一个更高的值。
或许这也能帮助你理解。尝试运行 查询where 0 =
,它将返回最高薪水,因为没有一个员工的薪水高于最高薪水。
或者,您可以保留2 =
但更改e1.salary < e2.salary
条件e1.salary <= e2.salary
以产生正确的结果,即像这样:
where 2 = ( select count(distinct salary)
from Employee e2
where e1.salary <= e2.salary)
Run Code Online (Sandbox Code Playgroud)