2011-05-05 138 views
26

我有一個應用程序需要在過去30天內顯示活動的條形圖。即使當天沒有活動,圖表也需要顯示所有日子。SQL Server:如何選擇日期範圍內的所有日期,即使某些日期沒有數據存在

例如:

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

我可以在查詢後做後期處理,以找出日期遺漏,並添加他們,但不知道是否有一個更簡單的方式做到這一點在SQL Server中。非常感謝

+4

提示:數字(又名日)表 – 2011-05-05 15:02:42

回答

34

您可以使用遞歸CTE建立你的30天列表中,然後加入,爲您的數據

--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 
... 
+2

+1的使用遞歸CTE來獲得一組值。 – 2011-05-05 15:25:25

+2

如果天數大於100,這不起作用。我得到錯誤「最大遞歸深度爲100」。是的,答案可以回答這個問題,但它可能會導致錯誤。 – 2012-12-05 12:50:50

+4

然後你的問題是不一樣的OP中有一個絕對的30個項目窗口,這種解決方案是好的,不會出錯。如果你想增加遞歸限制,只要告訴它這樣做; '選項(maxrecursion 100)' – 2012-12-05 13:02:17

1

要麼定義一個包含日期的靜態表,要麼動態地創建一個臨時表\表變量,以便在您使用的活動表中(包括)最小和最大日期之間存儲每個日期。

在兩個表之間使用外部聯接以確保日期表中的每個日期都反映在輸出中。

如果您使用靜態日期表,您可能希望將輸出的日期範圍限制爲圖中所需的範圍。

0

create a numbers table,並用它喜歡:

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 [email protected]+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) 
0

也許是這樣的: 創建DaysTable的內燃飢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 
9

使用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] 

這是假設一切是一個日期;如果是DateTime,則必須截斷(使用DATEADD(dd, 0, DATEDIFF(dd, 0, [DATE])))。

1

沒有的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 
0

對於那些用遞歸過敏

select SubQ.TheDate 
from 
(
    select DATEADD(day, a.a + (10 * b.a) + (100 * c.a), 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 a.a + (10 * b.a) + (100 * c.a) < 30 
) AS SubQ 
ORDER BY TheDate 
0

試試吧。

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 
0

我的場景比OP示例稍微複雜一點,所以我以爲我會分享來幫助其他有類似問題的人。我需要按日期對銷售訂單進行分組,而訂單則使用日期時間進行存儲。

因此,在「天」查詢表中,我無法真正將日期時間存儲爲'00:00:00.000'並獲得任何匹配。因此我以字符串形式存儲,並試圖直接加入轉換後的值。

這沒有返回任何零行,解決方案是做一個子查詢返回已經轉換爲一個字符串的日期。

示例代碼如下:

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 
1

@Alex K.的答案是完全正確的,但它並不適用於那些不支持遞歸版本一起使用公用表格表達式(就像我正在使用的版本)。在這種情況下,以下工作將完成。

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; 
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(t.a) as COUNT from calender c left join test t on c.date = t.a group by c.date

相關問題