【问题标题】:Find gaps in date ranges - TSQL查找日期范围内的空白 - SQL
【发布时间】:2017-04-06 07:31:53
【问题描述】:

我有以下 SQL,我希望在以下日期中有空缺。

declare @startdate datetime = '2017-05-01'
declare @enddate datetime = '2017-05-25'

create table #tmpdates (id int, date1 datetime, date2 datetime, rate int)

insert into #tmpdates values (1, '2017-05-05', '2017-05-15', 10)
insert into #tmpdates values (2, '2017-05-16', '2017-05-18', 12)
insert into #tmpdates values (3, '2017-05-21', '2017-05-25', 15)

select * from #tmpdates where date1 >= @startdate and date2 <= @enddate

drop table #tmpdates

所以输出应该包含 2017-05-01 到 2017-05-04 和 2017-05-19 到 2017-05-20 - 还有 2 条记录。

Output:
1   5/1/2017 0:00   5/4/2017 0:00   NO DATA
2   5/5/2017 0:00   5/15/2017 0:00  10
3   5/16/2017 0:00  5/18/2017 0:00  12
4   5/19/2017 0:00  5/20/2017 0:00  NO DATA
5   5/21/2017 0:00  5/25/2017 0:00  15

在我上面的查询中,只返回日期范围记录。请指导或者我如何也包括这些?

【问题讨论】:

  • 我真的很难理解你想在这里实现什么。你能再解释一下吗?
  • OP 有一个开始日期和一个结束日期,还有一个包含日期范围的表格。他想找到表格中不存在的开始日期和结束日期之间的范围。
  • @Tanner 这还不够。为什么是 1-4 和 19-20 而不是 17 和 21-24?
  • @tanner - 谢谢,为我做的 :)
  • 阅读:stackoverflow.com/questions/7812986/… 它描述了如何为@StartDate@EndDate 之间的所有日期生成行。一旦你有了这个LEFT JOIN 有间隙的表。

标签: sql-server tsql sql-server-2012


【解决方案1】:

这是在没有重叠间隔的假设下工作的。

declare @startdate datetime = '2017-05-16'
declare @enddate datetime = '2017-05-26'

create table #tmpdates (id int, date1 datetime, date2 datetime, rate int)

insert into #tmpdates values (0, '2017-04-01', '2017-04-25',22)
insert into #tmpdates values (1, '2017-05-05', '2017-05-15', 10)
insert into #tmpdates values (2, '2017-05-16', '2017-05-18', 12)
insert into #tmpdates values (3, '2017-05-21', '2017-05-25', 15)

declare @final_result table (date1 date, date2 date, rate int)

insert into @final_result 

select @startdate,dateadd(day,-1,t.date1),null
from #tmpdates t
where @startdate < t.date1 and 
        t.date1 <= (select min(t1.date1) from #tmpdates t1 where t1.date1 >= @startdate)

union all

select date1, date2, rate 
from #tmpdates 
where (date1 >= @startdate or date2 >= @startdate) and 
      (date2 <= @enddate or date1 <= @enddate)

union all

select dateadd(day,1,t.date2), 
        ( select dateadd(day,-1,min(t3.date1)) 
            from #tmpdates t3 where t3.date1 > t.date2) , 
        null
from #tmpdates t
where dateadd(day,1,t.date2) < (select min(t1.date1) from #tmpdates  t1 where t1.date1 > t.date2)
and t.date1 >= @startdate and t.date2 <= @enddate

union all

select dateadd(day,1,max(t.date2)), @enddate, null
from #tmpdates t
having max(t.date2) < @enddate


drop table #tmpdates

select * from @final_result order by date1

编辑

它从四个查询中收集数据并执行union all

第一个查询:

select @startdate,dateadd(day,-1,t.date1),null
from #tmpdates t
where @startdate < t.date1 and 
        t.date1 <= (select min(t1.date1) from #tmpdates t1 where t1.date1 >= @startdate)

选择@startdate 和表中第一个(最小)日期之间的间隔,如果@startdate 之前有间隔,则忽略它们。因此,它会选择从@startdate 到大于@startdate 的间隔的第一个日期之间的间隔(如果有)。

第二个查询:

select date1, date2, rate 
from #tmpdates 
where (date1 >= @startdate or date2 >= @startdate) and 
      (date2 <= @enddate or date1 <= @enddate)

从表中选择记录(非间隙)。如果@startdate 介于该范围之间,则包含该记录。 @enddate 参数也是如此。

第三个​​查询:

select dateadd(day,1,t.date2), 
        ( select dateadd(day,-1,min(t3.date1)) 
            from #tmpdates t3 where t3.date1 > t.date2) , 
        null
from #tmpdates t
where dateadd(day,1,t.date2) < (select min(t1.date1) from #tmpdates  t1 where t1.date1 > t.date2)
and t.date1 >= @startdate and t.date2 <= @enddate

选择桌子上最小和最大(位于@startdate@enddate 之间)间隔之间的间隙。

最后是第四个查询:

select dateadd(day,1,max(t.date2)), @enddate, null
from #tmpdates t
having max(t.date2) < @enddate

选择表格上最大日期(@startdate@enddate 之间的最大日期)与@enddate 之间的间隔(如果有间隔)。

所有这些记录都被插入到@final_result表中,这样它们就可以按区间排序了。

【讨论】:

  • 这仅适用于样本数据还是适用于真实世界更大的数据集?
  • @Tanner 我添加了它如何工作的解释。只要间隔没有重叠,它应该适用于任何数据集(我没有对此进行测试)。
  • 如果我通过声明 startdate datetime = '2017-05-01',则声明 enddate datetime = '2017-06-25' - 最后记录显示错误的日期范围。 @ahoxha
  • 是的,我想通了。正在努力解决这个问题。 :)
  • 如果我添加声明 startdate datetime = '2017-05-06' - 它应该包括它的范围。
【解决方案2】:

请使用以下查询:

DECLARE @STARTDATE DATE = '2017-05-01'
DECLARE @ENDDATE DATE = '2017-05-25'

DECLARE @DATES TABLE (ID INT, DATE1 DATE, DATE2 DATE, RATE INT)

INSERT INTO @DATES VALUES 
(1, '2017-05-05', '2017-05-15', 10),
(2, '2017-05-16', '2017-05-19', 12),
(3, '2017-05-21', '2017-05-25', 15)

SELECT* FROM 
(
    SELECT @STARTDATE AS DATE1,DATEADD(DAY,-1,MIN(DATE1)) AS DATE2,'NO DATA'AS RATE FROM @DATES
    UNION
    SELECT 
    CASE WHEN   
            LEAD(DATE1) OVER (ORDER BY DATE1) = DATEADD(DAY,1,DATE2) THEN NULL 
            ELSE DATEADD(DAY,1,DATE2) END AS DATE1,
    CASE WHEN   
            LEAD(DATE1) OVER (ORDER BY DATE1) = DATEADD(DAY,1,DATE2) THEN NULL 
            ELSE LEAD(DATEADD(DAY,-1,DATE1)) OVER (ORDER BY DATE1) END AS DATE2,
    'NO DATA'AS RATE
    FROM @DATES d
    UNION
    SELECT DATE1, DATE2,CAST(RATE AS NVARCHAR(10)) FROM @DATES
    UNION
    SELECT DATEADD(DAY,1,MAX(DATE2)) AS DATE1,@ENDDATE AS DATE2,'NO DATA'AS RATE FROM @DATES
) A WHERE A.DATE2 IS NOT NULL AND A.DATE1 <= A.DATE2
  AND DATE1 >= @STARTDATE AND DATE2 <=@ENDDATE
ORDER BY A.DATE1

【讨论】:

  • 这不会像@k-s 想要的那样在@startdate = '2017-05-01'@enddate = '2017-05-04' 的情况下返回一行。
  • 嗨@ahoxha,好点。我刚刚修改了代码。你现在可以检查一下。 :)
【解决方案3】:

你可以试试下面的代码。我正在从@StartDate 遍历到@endDate 并找到差距。

declare @startdate datetime = '2017-05-01'
declare @enddate datetime = '2017-05-04'
declare @startdate1 datetime, @enddate1 datetime
declare @dates table (date1 date,date2 date)
create table #tmpdates (id int, date1 datetime, date2 datetime, rate int)

insert into #tmpdates values (1, '2017-05-05', '2017-05-15', 10)
insert into #tmpdates values (2, '2017-05-16', '2017-05-18', 12)
insert into #tmpdates values (3, '2017-05-21', '2017-05-25', 15)

select * from #tmpdates where date1 >= @startdate and date2 <= @enddate
set @startdate1=@startdate
while @startdate1<=@enddate
begin
     if not exists(select 1 from #tmpdates where @startdate1 between date1 and date2)
     begin
          if not exists (select 1 from @dates where @startdate1 > date1 and date2 is null)
          begin
               insert into @dates(date1)values(@startdate1)
          end
          else
          begin
               if @startdate1+1>=@enddate
               begin
                   update @dates set date2=@startdate1 where date2 is null
               end
               set @startdate1+=1
          end
     end
     else
     begin 
         update @dates set date2=@startdate1-2 where date2 is null
     end
     set @startdate1+=1       
end
select * from
(select date1,date2, rate from #tmpdates
union
select *,0  as rate from @dates
) A WHERE date1>=@startdate and date2<=@enddate
drop table #tmpdates

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-22
    • 2016-10-08
    相关资源
    最近更新 更多