Як підрахувати різні стовпці в одній таблиці


15

Таблиця № 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;

16

Це типова стрижнева трансформація, і умовне агрегування, як запропонував Філ , є старим хорошим способом його здійснення.

Існує також більш сучасний синтаксис досягнення того ж результату, який використовує пункт 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 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, вам завжди потрібно пам’ятати про це одне:

  • Усі стовпці набору даних, що переводяться ( Claimsу даному випадку), які чітко не вказані у пункті PIVOT, є стовпцями GROUP BY .

Якщо 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 можна прочитати в посібнику:


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 всередині запиту, що здається особливо поганою ідеєю, якщо він просто використовується для форматування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.