如何获得同一表上不同列的计数

Kai*_*shu 16 sql-server pivot

表#01 Status

StatusID    Status
-----------------------
 1          Opened
 2          Closed
 3          ReOpened
 4          Pending
Run Code Online (Sandbox Code Playgroud)

表#02 Claims

ClaimID     CompanyName StatusID
--------------------------------------
1               ABC     1
2               ABC     1
3               ABC     2
4               ABC     4
5               XYZ     1
6               XYZ     1
Run Code Online (Sandbox Code Playgroud)

预期结果:

CompanyName TotalOpenClaims TotalClosedClaims TotalReOpenedClaims TotalPendingClaims
--------------------------------------------------------------------------------
ABC                 2           1                      0               1
XYZ                 2           0                      0               0
Run Code Online (Sandbox Code Playgroud)

我需要如何编写查询才能按预期获得结果?

Phi*_*lᵀᴹ 28

SUM()CASE语句最简单:

select CompanyName, 
sum(case when StatusID=1 then 1 else 0 end) as TotalOpenClaims,
sum(case when StatusID=2 then 1 else 0 end) as TotalClosedClaims,
sum(case when StatusID=3 then 1 else 0 end) as TotalReOpenedClaims,
sum(case when StatusID=4 then 1 else 0 end) as TotalPendingClaims
from Claims
group by CompanyName;
Run Code Online (Sandbox Code Playgroud)


And*_*y M 18

这是一个典型的枢轴转换,而条件聚合,正如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
;
Run Code Online (Sandbox Code Playgroud)

在内部,这种看起来更简单的语法等效于 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
;
Run Code Online (Sandbox Code Playgroud)

因此,本质上,PIVOT 查询是隐式 GROUP BY 查询。

然而,众所周知,PIVOT 查询在处理上比使用条件聚合的显式 GROUP BY 查询更棘手。当您使用 PIVOT 时,您需要始终牢记这一点:

  • ClaimsPIVOT 子句未明确提及的被透视数据集的所有列(在本例中)都是 GROUP BY 列

如果Claims仅包含示例中显示的三列,则上面的 PIVOT 查询将按预期工作,因为显然CompanyName是 PIVOT 中未明确提及的唯一列,因此最终成为隐式 GROUP BY 的唯一标准。

但是,如果Claims有其他列(例如,ClaimDate),它们将隐式用作额外的 GROUP BY 列 - 也就是说,您的查询本质上将执行

GROUP BY CompanyName, ClaimDate, ... /* whatever other columns there are*/`
Run Code Online (Sandbox Code Playgroud)

结果很可能不是您想要的。

不过,这很容易解决。为了排除不相关的列参与隐式分组,您可以只使用派生表,您将只选择结果所需的列,尽管这会使查询看起来不太优雅:

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
;
Run Code Online (Sandbox Code Playgroud)

尽管如此,如果Claims已经是派生表,则无需添加另一层嵌套,只需确保在当前派生表中仅选择生成输出所需的列。

您可以在手册中阅读有关 PIVOT 的更多信息: