Como obter contagem para diferentes colunas na mesma tabela

14

Tabela # 01 Status:

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

Tabela # 02 Claims:

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

Resultado esperado:

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

Como preciso escrever a consulta para obter o resultado conforme o esperado?

Kaishu
fonte

Respostas:

26

É mais fácil com SUM()e uma CASEdeclaração:

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;
Philᵀᴹ
fonte
15

Essa é uma transformação de pivô típica e a agregação condicional, como sugerido por Phil , é a boa e velha maneira de implementá-la.

Há também uma sintaxe mais moderna de obter o mesmo resultado, que usa a cláusula 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
;

Internamente, essa sintaxe aparentemente mais simples é equivalente à consulta GROUP BY de Phil. Mais exatamente, é equivalente a esta variação:

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
;

Portanto, uma consulta PIVOT é uma consulta GROUP BY implícita, essencialmente.

As consultas PIVOT, no entanto, são notoriamente mais complicadas no manuseio do que as consultas explícitas GROUP BY com agregação condicional. Ao usar o PIVOT, você sempre deve ter em mente o seguinte:

  • Todas as colunas do conjunto de dados que estão sendo dinâmicas ( Claimsneste caso) que não são explicitamente mencionadas na cláusula PIVOT são colunas GROUP BY .

Se Claimsconsistir apenas nas três colunas mostradas no seu exemplo, a consulta PIVOT acima funcionará conforme o esperado, porque aparentemente CompanyNameé a única coluna não mencionada explicitamente no PIVOT e, portanto, acaba como o único critério do GROUP BY implícito.

No entanto, se Claimshouver outras colunas (digamos ClaimDate), elas serão implicitamente usadas como colunas GROUP BY adicionais - ou seja, sua consulta estará essencialmente funcionando

GROUP BY CompanyName, ClaimDate, ... /* whatever other columns there are*/`

O resultado provavelmente não será o que você deseja.

Isso é fácil de corrigir, no entanto. Para excluir colunas irrelevantes da participação no agrupamento implícito, você pode apenas usar uma tabela derivada, na qual você selecionará apenas as colunas necessárias para o resultado, embora isso torne a consulta menos elegante:

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
;

Ainda, se Claimsjá é uma tabela derivada, não há necessidade de adicionar outro nível de aninhamento, apenas certifique-se de que na tabela derivada atual você esteja selecionando apenas as colunas necessárias para produzir a saída.

Você pode ler mais sobre o PIVOT no manual:

Andriy M
fonte
1

É certo que minha experiência é principalmente com o MySQL e não passei muito tempo no SQL Server. Eu ficaria muito surpreso se a seguinte consulta não funcionasse:

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;

Isso não fornece a saída no formato desejado, mas fornece todas as informações que você deseja, embora deixe de fora os zero casos. Isso me parece muito mais simples do que lidar com instruções CASE dentro de uma consulta, que parece uma péssima idéia se estiver sendo usada apenas para formatação.

Harageth
fonte