如何在SQL GROUP BY子句中返回空组

时间:2009-12-30 15:16:58

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

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

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

3 个答案:

答案 0 :(得分:2)

是的,构造一个表达式,仅返回adhoc的ordertotal,其他的返回0,另一个表示相反的表达式,并对这些表达式求和。这将包括每个位置一行,其中两列用于adhoc,一列用于Contracted ...

 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

答案 1 :(得分:0)

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

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组合的效果,但如果它是一次性或不经常使用的查询,则应该没问题。

答案 2 :(得分:0)

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

;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