2017-05-29 120 views
2

有一些非常相似的問題,但不一樣。 我要解決的下一個問題: 從表採用這種結構形式獲取幾日期之間的日期列表

| DATE_FROM | DATE_TO | 
|------------|------------| 
| 2010-05-17 | 2010-05-19 | 
| 2017-01-02 | 2017-01-04 | 
| 2017-05-01 | NULL  | 
| 2017-06-12 | NULL  | 

我需要得到像下面

| DATE_LIST | 
|------------| 
| 2010-05-17 | 
| 2010-05-18 | 
| 2010-05-19 | 
| 2017-01-02 | 
| 2010-01-03 | 
| 2010-01-04 | 
| 2017-05-01 | 
| 2017-06-12 | 

一個如何與SQL得到它的列表?的SQL Server 2016

+0

到目前爲止你試過的是什麼? – Jens

回答

3

另一種選擇是用CROSS APPLY和一個特設的理貨表

Select Date_List=B.D 
from YourTable A 
Cross Apply ( 
       Select Top (DateDiff(DAY,[DATE_FROM],IsNull([DATE_TO],[DATE_FROM]))+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),[DATE_FROM]) 
       From master..spt_values n1,master..spt_values n2 
      ) B 

返回

Date_List 
2010-05-17 
2010-05-18 
2010-05-19 
2017-01-02 
2017-01-03 
2017-01-04 
2017-05-01 
2017-06-12 
+0

請注意,master..spt_values沒有記錄,在Azure SQL數據庫中完全不存在。永久性的用戶定義號碼錶會更好。 –

+0

@DanGuzman同意,很明顯,一個理貨/日曆表將是要走的路。但是那就是說,任何可重新分配大小的表都可以。沒有提及任何字段名稱 –

3

一種方法是使用一個遞歸CTE:

with cte as (
     select date_from as date_list, date_to 
     from t 
     union all 
     select dateadd(day, 1, date_from), date_to 
     from cte 
     where date_from < date_to 
    ) 
select date_list 
from cte; 

默認情況下,遞歸CTE被限制爲100的遞歸深度(然後它返回一個錯誤)。這適用於長達100天的跨度。您可以使用OPTION (MAXRECURSION 0)刪除限制。

+0

也許我錯了,但是你的SQL代碼只返回第一列 –

+0

@ЕвгенийМ。 。 。不,它會產生一個錯誤。我需要選擇正確的列。 –

2

雖然你可以創建在查詢飛日期範圍,考慮創建一個永久日曆表。這將提供更好的性能,並可以通過其他屬性(如星期幾,財政季度等)進行擴展。您可以找到許多用互聯網搜索加載這種表的示例。

下面是一個有40年曆史的例子。

--example calendar table load script 
CREATE TABLE dbo.Calendar(
    CalendarDate date NOT NULL 
     CONSTRAINT PK_Calendar PRIMARY KEY 
    ); 
WITH 
    t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n)) 
    ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d) 
    ,t64k AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b) 
INSERT INTO dbo.Calendar WITH(TABLOCKX) 
SELECT DATEADD(day, num, '20000101') 
FROM t64k 
WHERE DATEADD(day, num, '20000101') < '20400101' 
GO 
DECLARE @example TABLE(
    DATE_FROM date NOT NULL 
    ,DATE_TO date NULL 
    ); 
GO 

--example query 
INSERT INTO @example VALUES 
     ('2010-05-17', '2010-05-19') 
    , ('2017-01-02', '2017-01-04') 
    , ('2017-05-01', NULL) 
    , ('2017-06-12', NULL) 
SELECT 
    c.CalendarDate 
FROM @example AS e 
JOIN dbo.Calendar AS c ON 
    c.CalendarDate BETWEEN e.DATE_FROM AND COALESCE(e.DATE_TO, e.DATE_FROM); 
相關問題