SQL如何编写返回缺失日期范围的查询?

时间:2018-07-09 21:39:58

标签: sql sql-server sql-server-2014

我试图弄清楚如何编写查询某些记录并查找今天 9999-12-31 之间的日期范围的查询。 我的数据如下:

ID      |start_dt                   |end_dt                     |prc_or_disc_1
10412   |2018-07-17 00:00:00.000    |2018-07-20 00:00:00.000    |1050.000000
10413   |2018-07-23 00:00:00.000    |2018-07-26 00:00:00.000    |1040.000000

因此对于此数据,我希望查询返回:

2018-07-10 | 2018-07-16
2018-07-21 | 2018-07-22
2018-07-27 | 9999-12-31

我不太确定从哪里开始。这可能吗?

7 个答案:

答案 0 :(得分:1)

select
    CASE WHEN DATEDIFF(day, end_dt, ISNULL(LEAD(start_dt) over (order by ID), '99991231')) > 1 then end_dt +1 END as F1,
    CASE WHEN DATEDIFF(day, end_dt, ISNULL(LEAD(start_dt) over (order by ID), '99991231')) > 1 then ISNULL(LEAD(start_dt) over (order by ID) - 1, '99991231') END as F2
from t

有效的SQLFiddle示例是-> Here

2008年版本

SELECT 
    X.end_dt + 1 as F1,
    ISNULL(Y.start_dt-1, '99991231') as F2
FROM t X
LEFT JOIN (
SELECT 
      *
    , (SELECT MAX(ID) FROM t WHERE ID < A.ID) as ID2
FROM t A) Y ON X.ID = Y.ID2
WHERE DATEDIFF(day, X.end_dt, ISNULL(Y.start_dt, '99991231')) > 1

有效的SQLFiddle示例是-> Here

答案 1 :(得分:1)

您可以使用MS SQL中的lag()函数来完成此操作(但是从2012年开始可用吗?)。

 with myData as
    (
      select *, 
      lag(end_dt,1) over (order by start_dt) as lagEnd
      from myTable),
    myMax as
    (
      select Max(end_dt) as maxDate from myTable
    )
    select dateadd(d,1,lagEnd) as StartDate, dateadd(d, -1, start_dt) as EndDate
    from myData
    where lagEnd is not null and dateadd(d,1,lagEnd) < start_dt
    union all
    select dateAdd(d,1,maxDate) as StartDate, cast('99991231' as Datetime) as EndDate 
    from myMax
    where maxDate < '99991231';

如果lag()在MS SQL 2008中不可用,则可以使用row_number()和加入来模拟它。

答案 2 :(得分:1)

http://sqlfiddle.com/#!18/65238/1

SELECT
  *
FROM
(
  SELECT
    end_dt+1                            AS start_dt,
    LEAD(start_dt-1, 1, '9999-12-31')
      OVER (ORDER BY start_dt)
                                        AS end_dt
  FROM
    yourTable
)
  gaps
WHERE
  gaps.end_dt >= gaps.start_dt

但是,我会 ,敦促您使用“独占”结束日期。也就是说,范围是除end_dt以外的所有内容。

这样,一天的范围变为'2018-07-09', '2018-07-10'

很明显,我的范围是一天,如果您从另一范围中减去一个范围,则可以得到一天。

此外,如果您更改为需要小时粒度或分钟粒度 ,则无需更改数据 。它只是工作。总是。可靠地。直观地。

如果您在网络上搜索,则会发现大量文档,从软件角度来看,为什么包容性开始和包容性结束是 非常 的好主意。 (然后,在上面的查询中,您可以删除不可靠的+1-1。)

答案 3 :(得分:1)

这应该在2008年有效,它假定表中的范围不重叠。它还将消除当前行的end_date是下一行的开始日期的前一天的行。

  with dtRanges as (
       select start_dt, end_dt, row_number() over (order by start_dt) as rownum 
       from table1
  )

  select t2.end_dt + 1, coalesce(start_dt_next -1,'99991231')
  FROM 
    (  select dr1.start_dt, dr1.end_dt,dr2.start_dt as start_dt_next
       from dtRanges dr1
       left join dtRanges dr2 on dr2.rownum = dr1.rownum + 1
    ) t2
  where 
  t2.end_dt + 1 <>  coalesce(start_dt_next,'99991231')

答案 4 :(得分:0)

您可能想看看以下内容: http://sqlfiddle.com/#!18/3a224/1 您只需将开始范围编辑为今天,将结束范围编辑为9999-12-31。

答案 5 :(得分:0)

这可以解决您的问题,但如果有重叠,边缘情况等,请提供一些示例数据。

在结束日期之后的第一天和下一行的开始日期的第一天之前。

DECLARE @ TABLE (ID int, start_dt DATETIME, end_dt DATETIME, prc VARCHAR(100))

INSERT INTO @ (id, start_dt, end_dt, prc)
VALUES 
(10410,   '2018-07-09 00:00:00.00','2018-07-12 00:00:00.000','1025.000000'),
(10412,   '2018-07-17 00:00:00.00','2018-07-20 00:00:00.000','1050.000000'),
(10413,   '2018-07-23 00:00:00.00','2018-07-26 00:00:00.000','1040.000000')


SELECT DATEADD(DAY, 1, end_dt)
, DATEADD(DAY, -1, LEAD(start_dt, 1, '9999-12-31') OVER(ORDER BY id) )
FROM @

答案 6 :(得分:0)

可以使用窗口功能LEAD

DATEDIFF和WHERE子句。

示例代码段

declare @Table table (ID int, start_dt datetime, end_dt datetime, prc_or_disc_1 float);

insert into @Table (ID, start_dt, end_dt, prc_or_disc_1) values
 (10410,'2018-07-09 00:00:00','2018-07-12 00:00:00',1025.0)
,(10412,'2018-07-17 00:00:00','2018-07-20 00:00:00',1050.0)
,(10413,'2018-07-23 00:00:00','2018-07-26 00:00:00',1040.0)
;

select 
 start_date, 
 coalesce(end_date, convert(date,'9999-12-31')) as end_date
from 
(
    select 
     convert(date, dateadd(day, 1, end_dt)) as start_date,
     convert(date, dateadd(day, -1, lead(start_dt) over (order by ID))) as end_date
    from @Table 
) q
where coalesce(datediff(day, start_date, end_date), 9) >= 0
order by start_date;