web-dev-qa-db-fra.com

Comment obtenir le nombre de différentes colonnes sur la même table

Tableau # 01 Status:

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

Tableau # 02 Claims:

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

Résultat attendu:

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

Comment dois-je écrire la requête afin d'obtenir le résultat attendu?

15
Kaishu

C'est plus simple avec SUM() et une instruction 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;
27
Philᵀᴹ

Il s'agit d'une transformation de pivot typique, et l'agrégation conditionnelle, comme suggérée par Phil , est la bonne vieille façon de l'implémenter.

Il existe également une syntaxe plus moderne pour obtenir le même résultat, qui utilise la clause 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
;

En interne, cette syntaxe sans doute plus simple est équivalente à la requête GROUP BY de Phil. Plus exactement, elle équivaut à cette variation:

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
;

Ainsi, une requête PIVOT est une requête GROUP BY implicite, essentiellement.

Les requêtes PIVOT, cependant, sont notoirement plus difficiles à gérer que les requêtes GROUP BY explicites avec agrégation conditionnelle. Lorsque vous utilisez PIVOT, vous devez toujours garder à l'esprit cette seule chose:

  • Toutes les colonnes de l'ensemble de données pivoté (Claims dans ce cas) qui ne sont pas explicitement mentionnées dans la clause PIVOT sont des colonnes GROUP BY .

Si Claims se compose uniquement des trois colonnes illustrées dans votre exemple, la requête PIVOT ci-dessus fonctionnera comme prévu, car apparemment CompanyName est la seule colonne non explicitement mentionnée dans PIVOT et se retrouve donc comme seul critère du GROUP BY implicite.

Cependant, si Claims a d'autres colonnes (disons, ClaimDate), elles seront implicitement utilisées comme colonnes GROUP BY supplémentaires - c'est-à-dire que votre requête fera essentiellement

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

Le résultat ne sera probablement pas ce que vous voulez.

C'est facile à résoudre, cependant. Afin d'exclure les colonnes non pertinentes de la participation au regroupement implicite, vous pouvez simplement utiliser une table dérivée, où vous ne sélectionnerez que les colonnes nécessaires au résultat, bien que cela rend la requête moins élégante:

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
;

Néanmoins, si Claims est déjà une table dérivée, il n'est pas nécessaire d'ajouter un autre niveau d'imbrication, assurez-vous simplement que dans la table dérivée actuelle, vous ne sélectionnez que les colonnes requises pour produire la sortie.

Vous pouvez en savoir plus sur PIVOT dans le manuel:

16
Andriy M

Certes, mon expérience concerne principalement MySQL et je n'ai pas passé beaucoup de temps sur SQL Server. Je serais très surpris si la requête suivante ne fonctionnait pas:

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;

Cela ne vous donne pas la sortie dans le format que vous voulez mais cela vous donne toutes les informations que vous voulez, même si vous omettez les cas zéro. Cela me semble beaucoup plus simple que de traiter des instructions CASE à l'intérieur d'une requête, ce qui semble être une idée particulièrement mauvaise si elle est simplement utilisée pour le formatage.

1
Harageth