2017-05-29 83 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); 
相关问题