【问题标题】:Multiple aggregate functions in one SQL query from the same table using different conditions在一个 SQL 查询中使用不同条件从同一个表中查询多个聚合函数
【发布时间】:2010-04-16 16:52:22
【问题描述】:

我正在创建一个 SQL 查询,该查询将根据两个聚合函数的值从表中提取记录。这些聚合函数从同一个表中提取数据,但过滤条件不同。我遇到的问题是 SUM 的结果比我只包含一个 SUM 函数要大得多。我知道我可以使用临时表创建此查询,但我只是想知道是否有一个只需要一个查询的优雅解决方案。

我创建了一个简化版本来演示这个问题。以下是表结构:

EMPLOYEE TABLE

EMPID
1
2
3

ABSENCE TABLE

EMPID   DATE       HOURS_ABSENT
1       6/1/2009   3
1       9/1/2009   1
2       3/1/2010   2

这里是查询:

SELECT
    E.EMPID
    ,SUM(ATOTAL.HOURS_ABSENT) AS ABSENT_TOTAL
    ,SUM(AYEAR.HOURS_ABSENT) AS ABSENT_YEAR

FROM
    EMPLOYEE E

    INNER JOIN ABSENCE ATOTAL ON
        ATOTAL.EMPID = E.EMPID

    INNER JOIN ABSENCE AYEAR ON
        AYEAR.EMPID = E.EMPID

WHERE
    AYEAR.DATE > '1/1/2010'

GROUP BY
    E.EMPID

HAVING
    SUM(ATOTAL.HOURS_ABSENT) > 10
    OR SUM(AYEAR.HOURS_ABSENT) > 3

任何见解将不胜感激。

【问题讨论】:

  • 这有点令人困惑。根据给定的数据,您的预期结果是什么?

标签: sql sql-server tsql aggregate-functions


【解决方案1】:
SELECT
    E.EMPID
    ,SUM(ABSENCE.HOURS_ABSENT) AS ABSENT_TOTAL
    ,SUM(case when year(Date) = 2010 then ABSENCE.HOURS_ABSENT else 0 end) AS ABSENT_YEAR

FROM
    EMPLOYEE E

    INNER JOIN ABSENCE ON
        ABSENCE.EMPID = E.EMPID

GROUP BY
    E.EMPID

HAVING
    SUM(ATOTAL.HOURS_ABSENT) > 10
    OR SUM(case when year(Date) = 2010 then ABSENCE.HOURS_ABSENT else 0 end) > 3

编辑:

这没什么大不了的,但我讨厌重复条件,所以我们可以重构如下:

Select * From
(
    SELECT
        E.EMPID
        ,SUM(ABSENCE.HOURS_ABSENT) AS ABSENT_TOTAL
        ,SUM(case when year(Date) = 2010 then ABSENCE.HOURS_ABSENT else 0 end) AS ABSENT_YEAR

    FROM
        EMPLOYEE E

        INNER JOIN ABSENCE ON
            ABSENCE.EMPID = E.EMPID

    GROUP BY
        E.EMPID
    ) EmployeeAbsences
    Where ABSENT_TOTAL > 10 or ABSENT_YEAR > 3

这样,如果你改变你的案例条件,它只会在一个地方。

【讨论】:

  • 非常感谢!你向我介绍了“案例”……你让我摆脱了我一直遇到的大问题!
【解决方案2】:

将不同的事物分开分组,加入组。

SELECT
  T.EMPID
  ,T.ABSENT_TOTAL
  ,Y.ABSENT_YEAR
FROM
    (
    SELECT
        E.EMPID
        ,SUM(A.HOURS_ABSENT) AS ABSENT_TOTAL
    FROM
        EMPLOYEE E
        INNER JOIN ABSENCE A ON A.EMPID = E.EMPID
    GROUP BY
        E.EMPID
    ) AS T
    INNER JOIN
    (
    SELECT
        E.EMPID
        ,SUM(A.HOURS_ABSENT) AS ABSENT_YEAR
    FROM
        EMPLOYEE E
        INNER JOIN ABSENCE A ON A.EMPID = E.EMPID
    WHERE
        A.DATE > '1/1/2010'
    GROUP BY
        E.EMPID
    ) AS Y
    ON T.EMPLID = Y.EMPLID
WHERE
    ABSENT_TOTAL > 10 OR ABSENT_YEAR > 3

此外,如果只有 SQL 关键字是大写字母而其余的不是,则可读性会增加。恕我直言。

【讨论】:

    【解决方案3】:
    SELECT E.EMPID , sum(ABSENT_TOTAL) , sum(ABSENT_YEAR)
    FROM
    (SELECT 
        E.EMPID 
        ,SUM(ATOTAL.HOURS_ABSENT) AS ABSENT_TOTAL 
        ,0 AS ABSENT_YEAR 
    
    FROM 
        EMPLOYEE E 
    
        INNER JOIN ABSENCE ATOTAL ON 
            ATOTAL.EMPID = E.EMPID 
    WHERE 
        AYEAR.DATE > '1/1/2010' 
    
    GROUP BY 
        E.EMPID 
    
    HAVING 
        SUM(ATOTAL.HOURS_ABSENT) > 10 
    
     UNION ALL
    
     SELECT 0    
        ,SUM(AYEAR.HOURS_ABSENT) 
     FROM 
        EMPLOYEE E    
    
           INNER JOIN ABSENCE AYEAR ON 
            AYEAR.EMPID = E.EMPID 
    
    GROUP BY 
        E.EMPID 
    
    HAVING  
        SUM(AYEAR.HOURS_ABSENT) > 3) a
    
        GROUP BY A.EMPID 
    

    【讨论】:

      【解决方案4】:

      值得注意的是,您实际上不必在CASE 表达式中为ELSE 大小写提供0 值,如Jeremy's answer 所示。因为聚合函数不聚合 NULL 值,所以您可以将其留空并使用 CASE 表达式仅指定要在聚合中包含的值,例如

      SELECT 
        e.empid,
        SUM(CASE WHEN <something> THEN atotal.hours_absent END) AS absent_total,
        SUM(CASE WHEN <something> THEN ayear.hours_absent END) AS absent_year
      FROM ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-31
        • 2021-03-17
        • 2013-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多