解决连续性的日期范围(GAPS和ISLANDS)

时间:2016-03-24 09:23:39

标签: sql sql-server sql-server-2008

我有一个如下表格,其中列X是给定开始和结束时段之间商品的价格。

X    START_DATE     END_DATE
------------------------------    
1    01-01-2014     01-01-2016
2    01-04-2014     01-05-2014
3    01-07-2014     01-08-2014

然而,第一个条目是针对更大的时间段定义的,只有在未定义月度期间的项目价格时才应考虑该条目,类似于第二和第三个记录或缺少某个范围。现在想要的输出是

X    START_DATE      END_DATE
------------------------------    
1    01-01-2014     01-04-2014
2    01-04-2014     01-05-2014
1    01-05-2014     01-07-2014
3    01-07-2014     01-08-2014
1    01-08-2014     01-01-2016

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

选中此项,如果您满意,请点击+1

-- Data Samples
declare @X table ( Price int, datefrom datetime, dateto datetime)
insert @X values ( 1, '1.1.2014','1.1.2016'),(2,'1.4.2014','1.5.2014'),(3,'1.7.2014','1.8.2014');

-- Check samples
select * from @X;

-- Query
with Dat as ( 
            select datefrom from @X
            union
            select dateto from @X 
)
, Periods as ( 
            select datefrom,dateto = LEAD(datefrom,1) over (order by datefrom) 
            from Dat
)
,val as ( select Pr.*,P.*
            from Periods P 
            cross apply ( select top 1 Price from @X
                        where P.datefrom between datefrom and dateto - 0.000001
                        order by DATEDIFF(day,datefrom,dateto)
            ) Pr

)

select * from val

输出

Price       datefrom                dateto
----------- ----------------------- -----------------------
1           2014-01-01 00:00:00.000 2016-01-01 00:00:00.000
2           2014-04-01 00:00:00.000 2014-05-01 00:00:00.000
3           2014-07-01 00:00:00.000 2014-08-01 00:00:00.000

(3 row(s) affected)

Price       datefrom                dateto
----------- ----------------------- -----------------------
1           2014-01-01 00:00:00.000 2014-04-01 00:00:00.000
2           2014-04-01 00:00:00.000 2014-05-01 00:00:00.000
1           2014-05-01 00:00:00.000 2014-07-01 00:00:00.000
3           2014-07-01 00:00:00.000 2014-08-01 00:00:00.000
1           2014-08-01 00:00:00.000 2016-01-01 00:00:00.000

(5 row(s) affected)

答案 1 :(得分:0)

;WITH cte AS (
    SELECT *
    FROM (VALUES
    (1, '2014-01-01', '2016-01-01'),
    (2, '2014-04-01', '2014-05-01'),
    (3, '2014-07-01', '2014-08-01')
    ) as t(X, [START_DATE], [END_DATE])
)
,dates AS (
SELECT  ROW_NUMBER() OVER ( ORDER BY d.[Date] ) as r,
        d.[Date]
FROM (
    SELECT c.START_DATE as [Date] FROM cte c
    UNION 
    SELECT c.END_DATE as [Date] FROM cte c
) as d)

SELECT  MAX(c.X) AS X,
        d.[Date] AS [START_DATE],
        d1.[Date] AS [END_DATE]
FROM dates d 
INNER JOIN dates d1 ON d.r = d1.r-1
LEFT JOIN cte c ON d.[Date] BETWEEN c.START_DATE and c.END_DATE AND d1.[Date] BETWEEN c.START_DATE and c.END_DATE
GROUP BY d.[Date], d1.[Date]

结果:

X           START_DATE END_DATE
----------- ---------- ----------
1           2014-01-01 2014-04-01
2           2014-04-01 2014-05-01
1           2014-05-01 2014-07-01
3           2014-07-01 2014-08-01
1           2014-08-01 2016-01-01

(5 row(s) affected)

但是如果你添加一些新范围来覆盖另一个范围(比如(6, '2014-07-15', '2015-08-01')),你应该改变那个查询。