Как подсчитать количество столбцов в одной и той же таблице?

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

Как мне нужно написать запрос, чтобы я мог получить ожидаемый результат?

Kaishu
источник

Ответы:

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;
Philᵀᴹ
источник
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
;

Внутренне этот, возможно, более простой на вид синтаксис эквивалентен запросу 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 внутри запроса, которые кажутся особенно плохой идеей, если они просто используются для форматирования.

Harageth
источник