Sql server 按查询在分组中包括缺少的月份

Sql server 按查询在分组中包括缺少的月份,sql-server,tsql,group-by,date-range,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), datena

我想我有一个棘手的问题…:

我正试图得到一个月的订单计数,即使是零。以下是问题查询:

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)

…但我的查询跳过了三月和五月的一行。我已经尝试了COALESCECOUNTOrderNumber,0和ISNULLCOUNTOrderNumber,0,但我很确定分组会导致它不起作用。

因为您的查询无法猜出您想要的月份,所以您需要将您想要的月份存储在某个位置,将它们与表连接,然后分组。 比如:

;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
然后将此表与您的表连接:

   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)

希望它能起作用

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


下面是一个使用递归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);
;

OrderTable似乎没有三月或五月的记录,因此仅从该表中选择无法返回这些月份的结果。可能的解决方法:如果你的数据库有一个主日期表,你可以利用它,或者你可以动态创建自己的日期表。就像@andyholaday所说的-创建一个1月…12月的查找表,然后进行左连接,你将得到缺少的月份。谢谢你的努力,但这对我不起作用。我原以为T1和T2被调换了,但我没能搞定。我看不出它是如何工作的,因为从OrderTable到加入months表仍然没有结果。如果我理解这个概念,我最多只能得到12个月的假期,这也不是我想要的。哇,太棒了!您的选择没有返回正确的订单数量,但是您的部件工作得很好。。。所以我刚刚离开,将我的SELECT作为子查询加入到结果中,它全年都非常有效!非常感谢这正是我需要的,这是一段很棒的代码。这个查询在LINQ中看起来怎么样?这对我来说确实是一场噩梦,但我的团队毫无理由地讨厌存储过程。但是没关系。当然,我想出了另一个办法。
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;
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);
;