即使select中没有结果,也会显示日期范围内的所有月份

时间:2016-10-19 20:24:24

标签: sql-server tsql

我有以下查询,日期范围介于' 09/01/16 '和' 12/30/16 '。在附图中,结果仅适用于 9月 10月,我仍希望显示 11月 12月(或计数 0

的给定date2范围

我该如何做到这一点?

enter image description here

declare @date1 date = '09/01/16'
declare @date2 date = '12/30/16'

select 
    count(distinct w.wkid) as WksCount
    ,DATENAME(MONTH, r.date) as [month]
    ,year(r.date) as [year]
from
    Workshop w
    left join Reservation r on r.wkid=w.wkid
    left join Registration reg on reg.wkid=w.wkid
where
    r.date between @date1 and @date2
group by
    DatePart(Month, r.date)
    ,DateName(Month, r.date)
    ,year(r.date)
order by
    year(r.date)
    ,DatePart(Month, r.date)

3 个答案:

答案 0 :(得分:1)

稍微不同 - 我认为更容易理解和理解 - 方法,您可以使用CTE构建您的日期表并从中加入以获取完整的月份列表。

我已在cte中指定了月初和月末,因此您可以joinon开始,而无需使用r.date条件中的功能如果您的Workshop表很大,则会触发,因为这些函数会禁止索引利用。

如果您的datetime字段为declare @date1 date = '20160801'; declare @date2 date = '20161231'; with Dates as ( select dateadd(m,datediff(m,0,@date1),0) as MonthStart ,dateadd(d,-1,dateadd(m,datediff(m,0,@date1)+1,0)) as MonthEnd union all select dateadd(m,1,MonthStart) ,dateadd(d,-1,dateadd(m,2,MonthStart)) from Dates where MonthStart < dateadd(m,datediff(m,0,@date2),0) ) select count(distinct w.wkid) as WksCount ,DATENAME(MONTH, d.MonthStart) as [month] ,year(d.MonthStart) as [year] from Dates d left join Reservation r on r.date between d.MonthStart and d.MonthEnd and r.date between @date1 and @date2 left join Workshop w on r.wkid=w.wkid left join Registration reg on reg.wkid=w.wkid group by DatePart(Month, r.date) ,DateName(Month, r.date) ,year(r.date) order by year(r.date) ,DatePart(Month, r.date); ,则需要调整以下值以处理该月最后一天的最后一毫秒的值:

  <a id="createCampaignBtn" data-target="#createCampaignModal" data-toggle="modal"
                               class="removeRight removeLeft buttonsA">
                                <div class="buttons removeRight centerTextOutHoriz">
                                    <span class="centerTextInner buttonRight">Start a new Campaign</span>
                                </div>
                            </a>

答案 1 :(得分:0)

这是Sean Lange所建议的主旨。

1:首先构建tally table

2:然后,计数表与左连接一起使用以获取日期之间的所有月份,并且您将数据链接到正确的月份。 (最小值和最大值包含在我的代码示例中)

因此,没有数据的月份除了月份数之外,每列中只有一条记录为空。 现在要使用月份名称而不是月份编号,请参阅this question以了解具体方法。

P.S:月份数是Tally.N

declare @date1 date = '09/01/16'
declare @date2 date = '12/30/16'

--1: Build a tally table with CTE
;WITH
N0(_)            AS (SELECT NULL UNION ALL SELECT NULL),
N1(_)            AS (SELECT NULL FROM N0 AS L CROSS JOIN N0 AS R),
Tally            AS (SELECT N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))  FROM N1 AS L CROSS JOIN N1 AS R)

--2: Select data using the tally
select 
    count(distinct w.wkid) as WksCount
    ,Tally.N as [monthNo]
from Tally,
LEFT JOIN Reservation   AS r    ON Tally.N = DATEPART(MONTH, r.date)
                                AND r.date >= @date1
                                AND r.daye <= @date2
LEFT JOIN Workshop      AS w    ON r.wkid = w.wkid
left join Registration  AS reg  ON w.wkid = reg.wkid
WHERE Tally.N >= DATEPART(MONTH, @date1)
AND  Tally.N <= DATEPART(MONTH, @date2)
GROUP BY
    Tally.N --N is the month number
ORDER BY
    Tally.N

答案 2 :(得分:0)

对于使用表变量作为“基表”的非常大的结果表来说更容易:

--add these lines before your select
declare @d datetime set @d=@date1
declare @t table(count int, month varchar(12), year int)
--fill the table with 0-month-year values
while @d < @date2
begin
    insert into @t values(0, datename(mm, @d), datepart(yy, @d))
    set @d = dateadd(mm, 1, @d)
end

然后将结果添加到表变量:

insert into @t 
select count(distinct w.wkid)
    ,DATENAME(MONTH, r.date)
    ,year(r.date)
from Workshop w
    left join Reservation r on r.wkid=w.wkid
    left join Registration reg on reg.wkid=w.wkid
where r.date between @date1 and @date2
group by DatePart(Month, r.date) ,DateName(Month, r.date), year(r.date)

最后获得所需的结果:

select sum(count) as count, month, year from @t
    group by year, month
    order by year desc, month desc 

来自相对较少的表变量的聚合查询比另一个LEFT JOIN便宜得多。如果你有很多句号,可以考虑使用#temporary table。

抱歉,我现在没有安装MSSqlServer来检查我的代码,但这个想法非常简单。