SQL Server:如何selectdate范围内的所有date,即使某些date没有数据存在

我有一个应用程序,需要显示过去30天的活动条形图。 即使当天没有活动,图表也需要显示所有的日子。

例如:

DATE COUNT ================== 1/1/2011 5 1/2/2011 3 1/3/2011 0 1/4/2011 4 1/5/2011 0 etc.... 

我可以在查询之后做后期处理,找出缺失的date并添加它们,但是想知道在SQL Server中是否有更简单的方法。 非常感谢

您可以使用recursionCTE构build30天的列表,然后将其join到您的数据中

 --test select cast('05 jan 2011' as datetime) as DT, 1 as val into #t union all select CAST('05 jan 2011' as datetime), 1 union all select CAST('29 jan 2011' as datetime), 1 declare @start datetime = '01 jan 2011' declare @end datetime = dateadd(day, 29, @start) ;with amonth(day) as ( select @start as day union all select day + 1 from amonth where day < @end ) select amonth.day, count(val) from amonth left join #t on #t.DT = amonth.day group by amonth.day >> 2011-01-04 00:00:00.000 0 2011-01-05 00:00:00.000 2 2011-01-06 00:00:00.000 0 2011-01-07 00:00:00.000 0 2011-01-08 00:00:00.000 0 2011-01-09 00:00:00.000 0 ... 

使用CTE:

 WITH DateTable AS ( SELECT CAST('20110101' AS Date) AS [DATE] UNION ALL SELECT DATEADD(dd, 1, [DATE]) FROM DateTable WHERE DATEADD(dd, 1, [DATE]) < cast('20110201' as Date) ) SELECT dt.[DATE], ISNULL(md.[COUNT], 0) as [COUNT] FROM [DateTable] dt LEFT JOIN [MyData] md ON md.[DATE] = dt.[DATE] 

这是假设一切都是一个date; 如果是DateTime,则必须截断(使用DATEADD(dd, 0, DATEDIFF(dd, 0, [DATE])) )。

可以定义一个包含date的静态表,也可以随时创build一个临时表\表variables,以便在您正在使用的活动表中(包括)最小和最大date之间存储每个date。

在两个表之间使用外部连接来确保date表中的每个date都反映在输出中。

如果使用静态date表,则可能需要将输出的date范围限制为图中所需的范围。

没有Transact-SQL:MS SQL 2005 – 获取一个月的所有日子列表:

在我的情况下,“20121201”是一个预定义的值。


  SELECT TOp (Select Day(DateAdd(day, -Day(DateAdd(month, 1, '20121201')), DateAdd(month, 1, '20121201')))) DayDate FROM ( SELECT DATEADD(DAY,ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1,'20121201') as DayDate FROM sys.objects s1 CROSS JOIN sys.objects s2 ) q 

@Alex K.的回答是完全正确的,但对于不支持recursion公用表expression式的版本(如我正在使用的版本)不起作用。 在这种情况下,以下将做的工作。

 DECLARE @StartDate datetime = '2015-01-01' DECLARE @EndDate datetime = SYSDATETIME() ;WITH days AS ( SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, @StartDate), 0)) as d FROM ( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1) n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1 FROM sys.all_objects ORDER BY [object_id] ) AS n ) select days.d, count(t.val) FROM days LEFT OUTER JOIN yourTable as t ON t.dateColumn >= days.d AND t.dateColumn < DATEADD(DAY, 1, days.d) GROUP BY days.d ORDER BY days.d; 

创build一个数字表,并使用它:

 declare @DataTable table (DateColumn datetime) insert @DataTable values ('2011-01-09') insert @DataTable values ('2011-01-10') insert @DataTable values ('2011-01-10') insert @DataTable values ('2011-01-11') insert @DataTable values ('2011-01-11') insert @DataTable values ('2011-01-11') declare @StartDate datetime SET @StartDate='1/1/2011' select @StartDate+Number,SUM(CASE WHEN DateColumn IS NULL THEN 0 ELSE 1 END) FROM Numbers LEFT OUTER JOIN @DataTable ON DateColumn=@StartDate+Number WHERE Number>=1 AND Number<=15 GROUP BY @StartDate+Number 

OUTPUT:

 ----------------------- ----------- 2011-01-02 00:00:00.000 0 2011-01-03 00:00:00.000 0 2011-01-04 00:00:00.000 0 2011-01-05 00:00:00.000 0 2011-01-06 00:00:00.000 0 2011-01-07 00:00:00.000 0 2011-01-08 00:00:00.000 0 2011-01-09 00:00:00.000 1 2011-01-10 00:00:00.000 2 2011-01-11 00:00:00.000 3 2011-01-12 00:00:00.000 0 2011-01-13 00:00:00.000 0 2011-01-14 00:00:00.000 0 2011-01-15 00:00:00.000 0 2011-01-16 00:00:00.000 0 (15 row(s) affected) 

也许这样的事情:创buildDaysTable的30天。 和DataTable包含“日”列和“计数”列。 然后离开他们。

 WITH DaysTable (name) AS ( SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 -- .. And so on to 30 ), DataTable (name, value) AS ( SELECT DATEPART(DAY, [Date]), [Count] FROM YourExampleTable WHERE [Date] < DATEADD (day , -30 , getdate()) ) SELECT DaysTable.name, DataTable.value FROM DaysTable LEFT JOIN DataTable ON DaysTable.name = DataTable.name ORDER BY DaysTable.name 

对于那些recursion过敏

 select SubQ.TheDate from ( select DATEADD(day, aa + (10 * ba) + (100 * ca), DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) - 30) AS TheDate from ( (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c ) WHERE aa + (10 * ba) + (100 * ca) < 30 ) AS SubQ ORDER BY TheDate 

尝试一下。

 DECLARE @currentDate DATETIME = CONVERT(DATE, GetDate()) DECLARE @startDate DATETIME = DATEADD(DAY, -DAY(@currentDate)+1, @currentDate) ;WITH fnDateNow(DayOfDate) AS ( SELECT @startDate AS DayOfDate UNION ALL SELECT DayOfDate + 1 FROM fnDateNow WHERE DayOfDate < @currentDate ) SELECT fnDateNow.DayOfDate FROM fnDateNow 

我的情况比OP示例稍微复杂一点,所以我想分享一下帮助其他有类似问题的人。 我需要按date对销售订单进行分组,而订单则使用date时间进行存储。

因此,在“天”查询表中,我不能真正将date时间存储为“00:00:00.000”并获得任何匹配。 所以我作为一个string存储,我试图直接join转换后的值。

这没有返回任何零行,解决scheme是做一个子查询返回已经转换为一个string的date。

示例代码如下所示:

 declare @startDate datetime = convert(datetime,'09/02/2016') declare @curDate datetime = @startDate declare @endDate datetime = convert(datetime,'09/09/2016') declare @dtFormat int = 102; DECLARE @null_Date varchar(24) = '1970-01-01 00:00:00.000' /* Initialize #days table */ select CONVERT(VARCHAR(24),@curDate, @dtFormat) as [Period] into #days /* Populate dates into #days table */ while (@curDate < @endDate ) begin set @curDate = dateadd(d, 1, @curDate) insert into #days values (CONVERT(VARCHAR(24),@curDate, @dtFormat)) end /* Outer aggregation query to group by order numbers */ select [Period], count(c)-case when sum(c)=0 then 1 else 0 end as [Orders], sum(c) as [Lines] from ( /* Inner aggregation query to sum by order lines */ select [Period], sol.t_orno, count(*)-1 as c from ( /* Inner query against source table with date converted */ select convert(varchar(24),t_dldt, @dtFormat) as [shipdt], t_orno from salesorderlines where t_dldt > @startDate ) sol right join #days on shipdt = #days.[Period] group by [Period], sol.t_orno ) as t group by Period order by Period desc drop table #days 

样品结果:

 Period Orders Lines 2016.09.09 388 422 2016.09.08 169 229 2016.09.07 1 1 2016.09.06 0 0 2016.09.05 0 0 2016.09.04 165 241 2016.09.03 0 0 2016.09.02 0 0 
 DECLARE @StartDate DATE = '20110101', @NumberOfYears INT = 1; DECLARE @CutoffDate DATE = DATEADD(YEAR, @NumberOfYears, @StartDate); CREATE TABLE Calender ( [date] DATE ); INSERT Calender([date]) SELECT d FROM ( SELECT d = DATEADD(DAY, rn - 1, @StartDate) FROM ( SELECT TOP (DATEDIFF(DAY, '2011-01-01', '2011-12-31')) rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id]) FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2 ORDER BY s1.[object_id] ) AS x ) AS y; create table test(a date) insert into test values('1/1/2011') insert into test values('1/1/2011') insert into test values('1/1/2011') insert into test values('1/1/2011') insert into test values('1/1/2011') insert into test values('1/2/2011') insert into test values('1/2/2011') insert into test values('1/2/2011') insert into test values('1/4/2011') insert into test values('1/4/2011') insert into test values('1/4/2011') insert into test values('1/4/2011') select c.date as DATE,count(ta) as COUNT from calender c left join test t on c.date = ta group by c.date