【问题标题】:What is the Logic Working Behind of this Query?这个查询背后的逻辑是什么?
【发布时间】:2017-01-10 17:48:13
【问题描述】:

我正在运行以下查询以从员工表中获得第三高的薪水,并且它工作正常,但我无法理解它的逻辑。子查询值如何与主查询匹配(左部分)。有人可以解释一下这个查询背后的逻辑是什么吗?

select e1.salary 
from employee as e1 
where 3 = (select count(salary) 
           from employee as e2 
           where e1.salary<=e2.salary)

PS:我可以理解count() 返回的行数(所有记录都是唯一的)。

【问题讨论】:

  • 对于了解非常基础的人;为什么要这么复杂?你不能用 RANK 或 ROW_NUMBER 和 ORDER BY 来完成这个吗?像 OP 示例那样做有继承价值吗?
  • @StianYttervik 我猜写它的人不知道 RANK 或 ROW_NUMBER 存在
  • 好的,关闭选民。你们都错了吗?这远不及是一个推荐问题。 (说真的,你从哪里得到的?)而且这个问题很清楚,有几个很好的答案解释了查询的作用。我什至认为这个问题不是“基本的”;当您仍在掌握 SQL 基础知识时,被该查询弄糊涂并不是没有道理的。 (哎呀,即使您熟悉 SQL,也可能需要花一分钟的时间盯着它看。)如果您要投票结束,首先找出一个好的理由
  • @jpmc26 - 我同意。我想问题出在标题上,而人们没有阅读除此之外的内容。如果它的标题是“如何选择表格中第三高的值?”它可能会获得更少的接近票。

标签: sql sql-server


【解决方案1】:

这个查询基本上是在说:

for each row in employee assign to e1
    count = 0
    for each row in employee assign to e2
        if e1.salary <= e2.salary
            count = count + 1
        end if
    end for
    if count = 3
        add e1 to result set
    end if
end for
return result set

总而言之,对于雇员表中的每一行,它都会第二次访问该表并计算工资较低或相等的行数。如果正好有 3 个,它将将该行添加到结果中。

值得指出的是,如果有不止一名员工拥有相同的薪水,这很可能会出错。您可能想要的是一个带有排名功能的查询。像这样的:

SELECT salary
FROM
    (SELECT 
        salary
        ,DENSE_RANK () OVER (ORDER BY salary DESC) [rank]
    FROM employee) t
WHERE
    [rank] = 3

“第三高”的确切含义可能有点模棱两可。如果我们的薪水为 8、8、6、5,上面将返回 5。如果我们想要 6,您需要将 DENSE_RANK 更改为 ROW_NUMBER,如下所示:

SELECT salary
FROM
    (SELECT 
        salary
        ,ROW_NUMBER () OVER (ORDER BY salary DESC) [rank]
    FROM employee) t
WHERE
    [rank] = 3

上面的 DENSE_RANK 版本也会在出现并列第三名时返回多行。这是否可取取决于确切的要求,但可以通过对薪水使用聚合函数来减少这种情况。

SELECT MAX(salary)
FROM
    (SELECT 
        salary
        ,DENSE_RANK() OVER (ORDER BY salary desc) [rank]
    FROM employee) t
WHERE
    [rank] = 3

【讨论】:

    【解决方案2】:

    考虑这些值:

    Salary:
    1
    2
    3
    4
    5
    6
    7
    8
    
    e1  e2
    1   1
    2   2
    3   3
    4   4
    5   5
    6   6
    7   7
    8   8
    

    对于e1.1e2 中有 8 行大于或等于 e1.1

    对于e1.2e2 中有 7 行大于或等于 e1.2

    ...

    对于e1.6e2 中有 3 行大于或等于 e1.6

    这是一个相当奇怪和令人困惑的选择语句。我只是用DENSE_RANK窗口函数重写它,因为如果你有几行相同的薪水,你不会得到正确的结果:

    DECLARE @t TABLE ( i INT )
    INSERT  INTO @t
    VALUES  ( 1 ),
            ( 2 ),
            ( 3 ),
            ( 4 ),
            ( 5 ),
            ( 6 ),
            ( 8 ),
            ( 8 );
    
    
    WITH    cte
              AS ( SELECT   * ,
                            DENSE_RANK() OVER ( ORDER BY i DESC ) AS rn
                   FROM     @t
                 )
        SELECT  *
        FROM    cte
        WHERE   rn = 3
    

    导致5,而您的初始选择语句将导致6,我认为这根本不是第三高的薪水。

    【讨论】:

    • 对于大多数人来说,“第三高薪水”是指薪水第三高的人的薪水,所以 5、6、8、8 的预期结果是 6。
    【解决方案3】:

    employeee1 中的每个薪水都将传递给sub-querySub-query 将找到所有小于传递的salarysalaries 并计算它。

    对于传递的薪水,如果子查询返回计数为3,则将产生该薪水

    假设员工表中有 5 条记录

    1
    2
    3
    4
    5
    6
    7
    8
    

    1e1传递过来时,子查询会像

    select e1.salary 
    from employee as e1 
    where 3 = (select count(salary) 
               from employee as e2 
               where 1<=e2.salary)
    

    现在子查询中的计数将是 8,因为所有记录都大于或等于 1。计数不等于 3,因此不会返回 salary 1


    2从e1传入时,子查询会是这样的

    select e1.salary 
    from employee as e1 
    where 3 = (select count(salary) 
               from employee as e2 
               where 2<=e2.salary)
    

    现在子查询中的计数将是 7,因为除了 1 之外,所有记录都大于或等于 2。计数不等于 3,因此不会返回 Salary 2


    6从e1传入时,子查询会是这样的

    select e1.salary 
    from employee as e1 
    where 3 = (select count(salary) 
               from employee as e2 
               where 6<=e2.salary)
    

    现在有三个大于或等于 6 的记录(即 6,7,8)所以计数将是 3 并且满足条件。所以salary 6会被返回

    【讨论】:

    • 我想在 Where 子句之后清除子查询过程,假设如果我有 4 条记录,例如 25000、15000、10000、17000。那么如果我想获得第三高的薪水,子查询将如何对这些值执行?
    • 你能解释一下每个 count() 的 e2.salary 值是多少
    • 根据您的回答,我可以理解 count() 是如何工作的,但无法理解它如何将条件应用于值。我在上面的评论中提到了我的桌子上有 4 个随机值。我想知道这些值在子查询中是如何处理的?
    【解决方案4】:

    其实很简单。第二个查询选择当前(选定)员工 (e1) 的薪水较低的所有员工。然后我们说工资较低或相同的员工人数需要为 3。这导致获得第三高的工资。

    【讨论】:

      猜你喜欢
      • 2020-05-22
      • 1970-01-01
      • 1970-01-01
      • 2017-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多