SQL Server: сгруппировать, чтобы получить строки с 0 - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть запрос, который возвращает список счетчиков, сгруппированных по месяцам, как показано ниже

 SELECT 
     CASE 
        WHEN MONTH(StartDate) = 1 THEN 'January'
        WHEN MONTH(StartDate) = 2 then 'February'                         
        WHEN MONTH(StartDate) = 3 then 'March'
        WHEN MONTH(StartDate) = 4 then 'April'
        WHEN MONTH(StartDate) = 5 then 'May'
        WHEN MONTH(StartDate) = 6 then 'June'
        WHEN MONTH(StartDate) = 7 then 'July'
        WHEN MONTH(StartDate) = 8 then 'August'
        WHEN MONTH(StartDate) = 9 then 'September'
        WHEN MONTH(StartDate) = 10 then 'October'
        WHEN MONTH(StartDate) = 11 then 'November'
        WHEN MONTH(StartDate) = 12 then 'December' 
        ELSE '' 
     END AS [month],
     COUNT(*) AS Count
 FROM
     Users
 WHERE
     YEAR(StartDate) = '2018'
 GROUP BY
     MONTH(StartDate)
 ORDER BY 
     MONTH(StartDate)

Результат выглядит примерно так:

Month    | Count
---------+-------
January  |   1
February |   2
April    |   7
May      |   3

Как видите, в списке отсутствуют месяцы, которые вы хотели бы получить. Ожидаемый результат:

Month    | Count
---------+-------
January  |   1
February |   2
March    |   0
April    |   7
May      |   3
June     |   0

Etc ...

Как я могу получить месяцы с нулевым счетом?

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Вы можете попытаться создать таблицу календаря, а затем сделать outer join.

и переместите условие YEAR(StartDate) = '2018' в ON из WHERE, потому что вам нужно использовать OUTER JOIN

и просто order by startnum столбец.

;WITH CTE AS (
   SELECT 1 startnum,12 endnum
   UNION ALL
   SELECT startnum +1 ,endnum
   FROM CTE 
   where startnum +1  <= endnum
)
SELECT 
     t1.dt as [month],
     Count(Users.StartDate) as Count
 FROM (
    SELECT DATENAME(month, DATEADD(month, startnum - 1, CAST('1900-01-01' AS date))) dt,startnum
    FROM CTE 
 ) t1 LEFT JOIN  Users on t1.startnum = MONTH(StartDate) AND YEAR(StartDate) = '2018'
 GROUP by t1.dt,t1.startnum
 ORDER BY t1.startnum

sqlfiddle

0 голосов
/ 14 ноября 2018

Создайте список дат начала месяца и используйте их в запросе:

with dates as (
      select cast('2018-01-01' as date) as dte, 1 as num
      union all
      select dateadd(month, 1, dte), num + 1
      from dates
      where num < 12
     )
select year(d.dte), datename(month, d.dte), count(u.startdate)
from dates d left join
     users u
     on month(u.startdate) >= month(d.dte)
where u.startdate >= '2018-01-01' and
      u.startdate < '2019-01-01' 
group by d.dte
order by d.dte;

Генерирует месяцы с использованием рекурсивного CTE.Обратите внимание на некоторые другие изменения:

  • Год явно включен в запрос, чтобы гарантировать, что запрос продолжает выполнять то, что вы ожидаете, даже если расширить диапазон дат.
  • диапазон дат определяется с использованием диапазона, а не year().Это лучшая практика, поэтому в запросе могут использоваться индексы.
  • Нет необходимости в выражении case, поскольку функции даты могут извлекать название месяца.
0 голосов
/ 14 ноября 2018

Вы можете использовать values construct & do left join:

SELECT mm.month, count(q.Count)
FROM ( VALUES ('January'), . . . , ('December') 
     ) mm (month) LEFT JOIN
     ( <query> 
     ) q
     ON q.month = mm.month;
...