在Group By查询中包含缺少的月份

时间:2012-07-14 00:23:10

标签: sql-server tsql group-by date-range

我想我在这里有一个艰难的...... :(

我试图按月计算订单数量,即使是零。这是问题查询:

SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber) AS Orders
FROM OrderTable
WHERE OrderDate >= '2012-01-01' and OrderDate <= '2012-06-30'
GROUP BY year(OrderDate), month(OrderDate), datename(month, OrderDate)

我希望得到的是这样的:

Month            Orders
-----            ------
January          10
February         7
March            0
April            12
May              0
June             5

...但是我的查询会在3月和5月跳过一行。我已经尝试了COALESCE(COUNT(OrderNumber), 0)ISNULL(COUNT(OrderNumber), 0),但我很确定这种分组会导致无效。

3 个答案:

答案 0 :(得分:23)

此解决方案不要求您对可能需要的月份列表进行硬编码,您只需提供任何开始日期和结束日期,它将为您计算月份边界。它包括输出中的年份,因此它将支持超过12个月,因此您的开始和结束日期可以跨越一年边界并仍然正确排序并显示正确的月份年。

DECLARE @StartDate SMALLDATETIME, @EndDate SMALLDATETIME;

SELECT @StartDate = '20120101', @EndDate = '20120630';

;WITH d(d) AS 
(
  SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0))
  FROM ( SELECT TOP (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
    FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT 
  [Month]    = DATENAME(MONTH, d.d), 
  [Year]     = YEAR(d.d), 
  OrderCount = COUNT(o.OrderNumber) 
FROM d LEFT OUTER JOIN dbo.OrderTable AS o
  ON o.OrderDate >= d.d
  AND o.OrderDate < DATEADD(MONTH, 1, d.d)
GROUP BY d.d
ORDER BY d.d;

答案 1 :(得分:2)

由于您的查询无法猜测您想要的月份,因此您需要将所需的月份存储在某个地方,将它们与您的表格连接,然后进行分组。 类似的东西:

;With Months (Month) 
AS
(

    select 'January' as Month
    UNION
    select 'February' as Month
    UNION
    select 'March' as Month
    UNION
    select 'April' as Month
    UNION
    select 'May' as Month
    UNION
    select 'June' as Month
    UNION
    select 'July' as Month
    UNION
    select 'August' as Month
    UNION
    select 'September' as Month
    UNION
    select 'October' as Month
    UNION
    select 'November' as Month
    UNION
    select 'December' as Month

)
--Also you could have them in a "Months" Table

然后只需JOIN此表与您的表格:

   Select 
    SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber) 
    FROM Months T1
    LEFT JOIN OrderTable T2 on datename(month, T2.OrderDate) = T2.Month
    WHERE (T2.OrderDate >= '2012-01-01' and T2.OrderDate <= '2012-06-30') 
OR T2.OrderDate IS NULL --So will show you the months with no rows
    GROUP BY year(T2.OrderDate), month(T2.OrderDate), datename(month, T2.OrderDate)

希望它有效!

答案 2 :(得分:1)

这是一个使用递归CTE:

declare @StartDate datetime = '2015-04-01';
declare @EndDate datetime = '2015-06-01';

-- sample data
declare @orders table (OrderNumber int, OrderDate datetime);
insert into @orders
select 11, '2015-04-02'
union all
select 12, '2015-04-03'
union all
select 13, '2015-05-03'
;

-- recursive CTE
with dates
as (
    select @StartDate as reportMonth
    union all
    select dateadd(m, 1, reportMonth)
    from dates
    where reportMonth < @EndDate
    )
select 
    reportMonth,
    Count = count(o.OrderNumber)
from dates
left outer join @orders as o 
    on o.OrderDate >= reportMonth
    and o.OrderDate < dateadd(MONTH, 1, reportMonth)
group by 
    reportMonth
option (maxrecursion 0);
;