【问题标题】:How to return empty groups in SQL GROUP BY clause如何在 SQL GROUP BY 子句中返回空组
【发布时间】:2010-12-31 03:24:26
【问题描述】:

我有一个查询返回在每个位置的合同和合同外花费了多少,它返回如下内容:

Location     | ContractStatus | Expenses
-------------+----------------+---------
New York     | Ad-hoc         | 2043.47
New York     | Contracted     | 2894.57
Philadelphia | Ad-hoc         | 3922.53
Seattle      | Contracted     | 2522.00

问题是,对于所有临时或所有合同费用的地点,我只能得到一行。我想为每个位置返回两行,如下所示:

Location     | ContractStatus | Expenses
-------------+----------------+---------
New York     | Ad-hoc         | 2043.47
New York     | Contracted     | 2894.57
Philadelphia | Ad-hoc         | 3922.53
Philadelphia | Contracted     |    0.00
Seattle      | Ad-hoc         |    0.00
Seattle      | Contracted     | 2522.00

有什么方法可以通过 SQL 来完成吗?这是我正在使用的实际查询(SQL Server 2005):

SELECT Location, 
     CASE WHEN Orders.Contract_ID IS NULL 
          THEN 'Ad-hoc' ELSE 'Contracted' END 
                     AS ContractStatus,
     SUM(OrderTotal) AS Expenses
FROM Orders
GROUP BY Location, 
    CASE WHEN Orders.Contract_ID IS NULL 
         THEN 'Ad-hoc' ELSE 'Contracted' END
ORDER BY Location ASC, ContractStatus ASC

【问题讨论】:

    标签: sql sql-server sql-server-2005


    【解决方案1】:

    是的,构造一个表达式,它只为 adhoc 返回 ordertotal,为其他返回 0,另一个相反的表达式,并对这些表达式求和。这将包括每个位置一行,两列一列用于临时,一列用于合同...

     SELECT Location,  
         Sum(Case When Contract_ID Is Null Then OrderTotal Else 0 End) AdHoc,
         Sum(Case When Contract_ID Is Null Then 0 Else OrderTotal  End) Contracted
     FROM Orders 
     GROUP BY Location
    

    如果你真的想为每个单独的行,那么一种方法是:

     SELECT Location, Min('AdHoc') ContractStatus,
         Sum(Case When Contract_ID Is Null 
                  Then OrderTotal Else 0 End) OrderTotal
     FROM Orders 
     GROUP BY Location
     Union
     SELECT Location, Min('Contracted') ContractStatus,
         Sum(Case When Contract_ID Is Null 
                  Then 0 Else OrderTotal  End) OrderTotal
     FROM Orders 
     GROUP BY Location
     Order By Location
    

    【讨论】:

    • 谢谢,这行得通。不幸的是,我确实需要单独的行,因为我正在浏览一个一次绘制一行图表的报告包。 :(
    • Min('AdHoc') ContractStatus 是干什么用的? Min() 不是数值吗?该查询与'AdHoc' AS ContractStatus 一样有效
    • 我不确定它会不会,因为通常 Group By 查询中不在 Group By 子句中的任何输出列都必须使用 Aggregate 函数...我猜该规则不适用到从常量值生成的列(如“ADHOC”)
    【解决方案2】:

    您需要先填充不同位置和合同的列表。看到这个查询,它可能有一些语法错误,因为我没有运行它。

    ;WITH Locations AS 
    (
        SELECT DISTINCT Location, 1 as ContractStatus
        FROM    Orders 
        UNION 
        SELECT DISTINCT Location, 2 as ContractStatus
        FROM    Orders
    )
    
    SELECT  Location, 
            CASE 
                WHEN L.ContractStatus = 1 THEN 'Ad-hoc'
                ELSE 'Contracted'
            END, 
            ISNULL(SUM(OrderTotal),0) Expenses
    FROM    Locations L
        LEFT JOIN Orders O 
            ON L.Location = O.Location AND L.ContractStatus = CASE WHEN O.Contract_ID IS NULL THEN 1 ELSE 2 END
    GROUP BY    L.Location, 
                CASE 
                    WHEN L.ContractStatus = 1 THEN 'Ad-hoc'
                    ELSE 'Contracted'
                END 
    

    【讨论】:

    • fyi- 它在SELECT Location 行上给出错误。它必须是SELECT L.Location
    【解决方案3】:

    在分组之前,您必须以某种方式为每个位置创建至少一行的每个状态。这是一种方法:

    SELECT s.Location, s.ContractStatus, ISNULL(o.Expenses, 0) AS Expenses
    FROM
    (
        SELECT
            Location,
            CASE
                WHEN Contract_ID IS NULL THEN 'Ad-hoc'
                ELSE 'Contracted'
            END AS ContractStatus,
            SUM(OrderTotal) AS Expenses
        FROM Orders
        GROUP BY
            Location,
            CASE WHEN Contract_ID IS NULL THEN 'Ad-hoc' ELSE 'Contracted' END)
    ) o
    RIGHT JOIN
    (
        SELECT DISTINCT o.Location, s1.ContractStatus
        FROM Orders o
        CROSS JOIN
        (
            SELECT 'Ad-hoc' AS ContractStatus
            UNION ALL
            SELECT 'Contracted'
        ) s1
    ) s
    ON s.Location = o.Location
    AND s.ContractStatus = o.ContractStatus
    

    编辑:我不确定 DISTINCT/CROSS JOIN 组合的性能,但如果它是一次性或不经常使用的查询,应该没问题。

    【讨论】:

    • 这有点问题。对于每个城市,我为每个临时总计和每个其他城市的每个合同总计获得一行。换句话说,我得到:NY Ad-hoc $2043,NY Ad-hoc $3922,NY Contr。 2894 美元,纽约合约。 2522 美元。每个城市我得到相同的四行。
    • 糟糕,我忘记了其中一个加入条件。我更新了答案。
    【解决方案4】:

    以前的大多数解决方案都依赖于确切知道第二列有多少,虽然我不是 SQL 专家,但我有一个用例,其中每个 @ 的 ContractStatus 的数量是未知的987654324@.

    所以我找到了How to merge 2 columns with all possible combinations in SQL?,它展示了如何处理任意数量的不同次要指标。

    http://sqlfiddle.com/#!18/72236b/3/0

    CREATE TABLE Table1
        ([Location] varchar(12), [ContractStatus] varchar(10), [Expenses] int)
    ;
        
    INSERT INTO Table1
        ([Location], [ContractStatus], [Expenses])
    VALUES
        ('New York', 'Ad-hoc', 2043.47),
        ('New York', 'Contracted', 2894.57),
        ('Philadelphia', 'Ad-hoc', 3922.53),
        ('Seattle', 'Contracted', 2522.00)
    ;
    
    -- Use CTE to get all possibilities
    with possibilities as (
        select l.Location, c.ContractStatus
        from (select distinct Location from [Table1]) l cross join
             (select distinct ContractStatus from [Table1]) c
    )
    select
        p.Location
        ,p.ContractStatus
        -- Choose the proper agg function here
        ,sum([Expenses]) as Expenses
    from possibilities p 
    left join [Table1] t on p.Location = t.Location and p.ContractStatus = t.ContractStatus
    group by p.Location, p.ContractStatus
    order by Location, ContractStatus
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 1970-01-01
      • 1970-01-01
      • 2012-11-23
      • 2010-11-29
      相关资源
      最近更新 更多