同じテーブルの異なる列のカウントを取得する方法


14

表#01 Status

StatusID    Status
-----------------------
 1          Opened
 2          Closed
 3          ReOpened
 4          Pending

表#02 Claims

ClaimID     CompanyName StatusID
--------------------------------------
1               ABC     1
2               ABC     1
3               ABC     2
4               ABC     4
5               XYZ     1
6               XYZ     1

期待される結果:

CompanyName TotalOpenClaims TotalClosedClaims TotalReOpenedClaims TotalPendingClaims
--------------------------------------------------------------------------------
ABC                 2           1                      0               1
XYZ                 2           0                      0               0

期待どおりの結果を得るために、どのようにクエリを書く必要がありますか?

回答:


26

次のステートメントを使用する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;

15

これは典型的なピボット変換であり、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クエリです。

ただし、PIVOTクエリは、条件付き集計を使用した明示的なGROUP BYクエリよりも処理が難しいことで有名です。PIVOTを使用している場合、次の1つを常に念頭に置く必要があります。

  • ClaimsPIVOT句で明示的に言及されていない(この場合)ピボットされるデータセットのすべての列はGROUP BY列です。

場合はClaims期待どおりの例に示す3つの列のみ、意志の仕事上の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の詳細については、マニュアルをご覧ください。


1

確かに私の経験は主にMySQLであり、SQL Serverにあまり時間を費やしていません。次のクエリが機能しなかった場合、私は非常に驚くでしょう:

SELECT 
  CompanyName, 
  status, 
  COUNT(status) AS 'Total Claims' 
FROM Claim AS c 
  JOIN Status AS s ON c.statusId = s.statusId 
GROUP BY 
  CompanyName, 
  status;

これは、必要な形式で出力を提供するわけではありませんが、ゼロのケースは除外しますが、必要な情報はすべて提供します。これは、クエリ内でCASEステートメントを処理するよりもはるかに簡単に感じます。CASEステートメントは、書式設定に使用するだけの場合は特に悪い考えのように感じます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.