根据聚合值选择单个记录

时间:2016-08-21 12:23:08

标签: sql sql-server

假设我有以下表格

EmployeeID            Salary                      Date
-----------           ----------------            -----------
37                    45000.00                    2015-03-11 
102                   36500.00                    2015-03-11 
103                   43000.00                    2015-03-11 
104                   45000.00                    2015-03-11 
105                   40000.00                    2015-03-11 
37                    45000.00                    2015-04-11 
102                   36500.00                    2015-04-11 
103                   43000.00                    2015-04-11 
104                   45000.00                    2015-04-11 
105                   40000.00                    2015-04-11 

我想要检索"薪酬总和"交叉80000,所以期望的输出是

EmployeeID            Salary                      Date
-----------           ----------------            -----------
37                    45000.00                    2015-03-11 
103                   43000.00                    2015-03-11 
104                   45000.00                    2015-03-11 
37                    45000.00                    2015-04-11 
103                   43000.00                    2015-04-11 
104                   45000.00                    2015-04-11 

我是通过以下方式实现的:

  1. 将汇总结果存储到表变量中。
  2. INNER加入原始表格和&表变量

    DECLARE @tmpAggregatedSalaries TABLE
    (
        EmployeeID          INT,
        SumOfSalary         DECIMAL(18, 2)
    )
    
    INSERT INTO @tmpAggregatedSalaries
    SELECT sal.EmployeeID
        , SUM(sal.Salary) AS SumOfSalary
    FROM Salaries sal
    GROUP BY sal.EmployeeID
    
    SELECT sal.*
        FROM Salaries sal
        INNER JOIN @tmpAggregatedSalaries aggrSal ON sal.EmployeeID = aggrSal.EmployeeID
            AND aggrSal.SumOfSalary > 80000
    
  3. 据我所知,存储临时结果以进行处理优先于内联查询,因此我选择"表变量"。请为我推荐更多优化版本。

2 个答案:

答案 0 :(得分:1)

一种选择是使用GROUP BY子查询来识别薪水总和大于80000的所有员工,以过滤掉您不想看到的表中的记录。

SELECT t1.EmployeeID,
       t1.Salary,
       t1.Date
FROM Salaries t1
INNER JOIN
(
    SELECT EmployeeID
    FROM Salaries
    GROUP BY EmployeeID
    HAVING SUM(Salary) > 80000
) t2
    ON t1.EmployeeID = t2.EmployeeID

答案 1 :(得分:1)

您还可以使用SUM() window功能:

CREATE TABLE dbo.Salaries
(
    EmployeeID INT
    , Salary DECIMAL(10, 2)
    , [Date] DATE
    , CONSTRAINT PK_Salaries PRIMARY KEY (EmployeeID, [Date])
);

INSERT INTO dbo.Salaries (EmployeeID, Salary, [Date])
VALUES (37, 45000.00, '2015-03-11')
    , (102, 36500.00, '2015-03-11')
    , (103, 43000.00, '2015-03-11')
    , (104, 45000.00, '2015-03-11')
    , (105, 40000.00, '2015-03-11')
    , (37,  45000.00, '2015-04-11')
    , (102, 36500.00, '2015-04-11')
    , (103, 43000.00, '2015-04-11')
    , (104, 45000.00, '2015-04-11')
    , (105, 40000.00, '2015-04-11');

SELECT S.EmployeeID, S.Salary, S.[Date]
FROM (
    SELECT EmployeeID, Salary, [Date], SUM(Salary) OVER(PARTITION BY EmployeeID) AS SalarySum
    FROM dbo.Salaries
    ) AS S
WHERE S.SalarySum > 80000;

此查询将计算每位员工的总薪水,之后只会过滤掉总收入少于80000的员工。

结果:

EmployeeID  Salary                                  Date
----------- --------------------------------------- ----------
37          45000.00                                2015-03-11
37          45000.00                                2015-04-11
103         43000.00                                2015-03-11
103         43000.00                                2015-04-11
104         45000.00                                2015-04-11
104         45000.00                                2015-03-11

当然,您可以使用Tim建议的内联查询:

SELECT S.EmployeeID, S.Salary, S.[Date]
FROM dbo.Salaries AS S
INNER JOIN (
    SELECT EmployeeID, SUM(Salary) AS SalarySum
    FROM dbo.Salaries
    GROUP BY EmployeeID
    ) AS T
    ON T.EmployeeID = S.EmployeeID
WHERE T.SalarySum > 80000;

对于这样小的结果集,后者似乎通过查看执行计划表现更好。您必须将其与实际数据进行比较,以确定哪个数据表现更好。为两个查询附加计划:

使用窗口函数:

enter image description here

经典之一:

enter image description here