같은 테이블에서 다른 열의 개수를 얻는 방법


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()a와 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

이것은 전형적인 피벗 변환이며 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을 사용할 때는 항상 다음 사항을 명심해야합니다.

  • ClaimsPIVOT 절에서 명시 적으로 언급되지 않은 피벗 된 ( 이 경우) 데이터 세트의 모든 열 은 GROUP BY 열 입니다.

Claims예제에 표시된 세 개의 열로만 구성된 경우 위의 PIVOT 쿼리는 예상대로 작동합니다. PIVOT CompanyName에서 명시 적으로 언급되지 않은 유일한 열이므로 암시 적 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 문을 처리하는 것보다 훨씬 간단합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.