如何在聚合后向 SELECT 查询添加额外的列而不将它们包含在 GROUP BY 中?

How can I add additional columns to a SELECT query after an aggregation without including them in GROUP BY?

这是我要查询的table:

EmployeeId, Salary, Date
8, 500, 2016-11-02
8, 500, 2016-09-21
8, 500, 2016-10-18
9, 500, 2016-10-18
9, 500, 2016-09-21
9, 500, 2016-11-02
10, 1000, 2016-11-02
10, 700, 2016-09-21

我想要 select 一份员工名单,以及他们在该特定员工可用的最新日期的薪水。在示例数据的情况下,这是相同的日期 (11-02),但情况不一定如此。我的查询:

SELECT EmployeeId, Salary, MAX(Date)
FROM table
GROUP BY EmployeeId. Salary
ORDER BY EmployeeId

正在返回:

8, 500, 2016-11-02
9, 500, 2016-11-02
10, 700, 2016-09-21
10, 1000, 2016-11-02

我的预期输出是:

8, 500, 2016-11-02
9, 500, 2016-11-02
10, 1000, 2016-11-02

如果我在没有薪水的情况下进行汇总,那么我会得到预期的结果,但我需要薪水可见。有没有办法将它包含在 post-聚合中?

您可以使用 window 函数 Row_Number() 查看最后一个值

Select * 
 From ( 
        Select *,RN = Row_Number() over (Partition By EmployeeId Order by Date Desc)
         From  YourTable
      ) A
 Where RN=1

您可以使用window函数来挑选最新的:

with cte as (
    select EmployeeId, 
        Salary,
        Date
        ROW_NUMBER() OVER (PARTITION BY EmployeeId ORDER BY Date DESC) RN
    from [table]
)
select EmployeeId, 
    Salary,
    Date
from cte
where RN = 1

如果不清楚,您基本上是 "partitioning" 按 EmployeeId,按日期降序对每个分区中的行进行编号,然后选择此编号为 1 的行(即每个员工的最新行) ).

在 window 函数之前(因为并非所有 RDBMS 版本都支持它们),您可以使用内联视图来执行此操作。

首先考虑您的需求:

  • 您需要一个包含员工的最大日期和员工 ID 的数据集。
  • 您需要 table 中的原始数据,您无法通过分组...

为了实现第一个目标,我们确实生成了一个内联视图(下面别名为 "B")。 然后我们加入回基集以检索额外的所需信息;允许内部连接消除不需要的记录;并完全否定对外部查询进行分组的需要。

SELECT A.EmployeeId, A.Salary, A.Date
FROM table A
INNER JOIN (SELECT max(date) mDate, EmployeeID 
            FROM table
            GROUP BY EmployeeID) B
  on A.EmployeeID = B.EmployeeID
 and A.Date = B.MDate
ORDER BY EmployeeId

在使用 RDBMS 时,请从集合的角度思考数据,以及如何过滤这些集合并将其重新组合在一起以实现所需的结果。大多数(不是所有)RDBMS 活动的基于集合的处理将是最有效的。随着 Window 函数(又称分析函数)的引入,无需子查询即可生成集合;使它们在数据分析中具有强大的功能;尽管最初很难让你的头脑清醒过来。

那么:ROW_NUMBER() OVER (PARTITION BY EmployeeId ORDER BY Date DESC) RN 究竟是做什么的?

它按照日期降序为遇到的每个新员工(分区)分配一个从 1 开始的行号(因此每个员工的最新日期的 RN 始终为 1!)但是,1 不是t 可用于 where 子句中的查询,因为 SQL 操作顺序生成最后的 select 值(因此 where 子句已经执行)。这意味着要使 window 函数达到预期的结果并限制为 RN=1,您必须将其包装在子查询中,然后您可以访问 RN = 1,从而只返回具有最新日期的员工的记录。由于 RDBMS 能够独立于分析函数执行 table 的生成,因此它可以同时处理它们并非常快速地提供响应。

我倾向于使用 APPLY 进行这些操作。

SELECT t1.EmployeeId, t2.Salary, t2.Date
FROM table t1
CROSS APPLY (SELECT TOP 1 Salary, Date
             FROM table
             WHERE EmployeeId = t1.EmployeeId
             ORDER BY Date DESC) t2
ORDER BY t1.EmployeeId