这是一个典型的枢轴转换,Phil提出的条件聚合是实现它的一种好方法。
为了达到相同的结果,还有一种更现代的语法,它使用PIVOT子句:
SELECT
CompanyName,
TotalOpenClaims = [1],
TotalClosedClaims = [2],
TotalReOpenedClaims = [3],
TotalPendingClaims = [4]
FROM
dbo.Claims
PIVOT
(
COUNT(ClaimID)
FOR StatusID IN ([1], [2], [3], [4])
) AS p
;
在内部,这种看起来更简单的语法等同于Phil的GROUP BY查询。更确切地说,它等效于以下变体:
SELECT
CompanyName,
TotalOpenClaims = COUNT(CASE WHEN StatusID = 1 THEN ClaimID END),
TotalClosedClaims = COUNT(CASE WHEN StatusID = 2 THEN ClaimID END),
TotalReOpenedClaims = COUNT(CASE WHEN StatusID = 3 THEN ClaimID END),
TotalPendingClaims = COUNT(CASE WHEN StatusID = 4 THEN ClaimID END)
FROM
dbo.Claims
GROUP BY
CompanyName
;
因此,PIVOT查询本质上是隐式的GROUP BY查询。
但是,与具有条件聚合的显式GROUP BY查询相比,PIVOT查询在处理方面非常棘手。使用PIVOT时,请务必牢记这一件事:
Claims
在PIVOT子句中未明确提及的数据透视表的所有列(在这种情况下)都是GROUP BY columns。
如果Claims
仅由示例中显示的三列组成,则上面的PIVOT查询将按预期工作,因为显然这CompanyName
是PIVOT中未明确提及的唯一列,因此最终成为隐式GROUP BY的唯一条件。
但是,如果Claims
还有其他列(例如ClaimDate
),则它们将隐式用作其他GROUP BY列-也就是说,您的查询实际上将
GROUP BY CompanyName, ClaimDate, ... /* whatever other columns there are*/`
结果很可能不是您想要的。
不过,这很容易解决。为了排除不相关的列参与隐式分组,您可以仅使用派生表,在该表中,您将只选择结果所需的列,尽管这会使查询看起来不太美观:
SELECT
CompanyName,
TotalOpenClaims = [1],
TotalClosedClaims = [2],
TotalReOpenedClaims = [3],
TotalPendingClaims = [4]
FROM
(SELECT ClaimID, CompanyName, StatusID FROM dbo.Claims) AS derived
PIVOT
(
COUNT(ClaimID)
FOR StatusID IN ([1], [2], [3], [4])
) AS p
;
但是,如果Claims
已经是派生表,则无需添加另一级嵌套,只需确保在当前派生表中仅选择生成输出所需的列即可。
您可以在手册中阅读有关PIVOT的更多信息: