查找员工工资第二高

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)以什么方式相互比较?

实际上这是执行计划,谁能解释更多步骤?

在此处输入图片说明

Pau*_*ite 7

我将解决问题的这一部分:

...这是执行计划 谁能解释更多步骤?

假设表定义如下所示:

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)返回的所有行(没有特定的顺序)。对于每一行:
    • 嵌套循环连接 (3) 执行内侧,将当前E1.Salary值作为参数传递。
    • 所述E2表扫描(8)发现的行匹配E2.Salary > E1.Salary
    • Distinct Sort (7) 按 对行进行分组E2.Salary
    • Stream Aggregate (6) 计算行数(每个不同的 1 个E2.Salary)。新的计数列被赋予标签Expr1008
    • 计算标量 (5) 将本机计数数据类型 ( ) 转换为bigintinteger因为查询指定COUNT而不是COUNT_BIG。新列的标签为Expr1004
  • 当行从嵌套循环连接中出现时,过滤器 (2) 应用谓词 Expr1004 = 1
  • 最后的 Distinct Sort 返回不同的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 和E2Table Scan(带谓词),它们如前所述过滤E2(使用 的当前值E1.Salary)、删除重复E2.Salary值并计算结果行。Lazy Index Spool 保存重新计算结果,以防Salary表中的值相同E1在嵌套循环连接的下一次迭代中出现。

因为 Sort 从E1by排序的行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 和E2Table 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)