计算两个日期之间的缺失日期范围和重叠日期范围

时间:2009-06-22 06:18:34

标签: sql sql-server algorithm tsql

我的数据库中有以下日期(dd / MM / yyyy)匹配事件:

eventId     startDate     endDate
1           02/05/2009    10/05/2009
2           08/05/2009    12/05/2009
3           10/05/2009    12/05/2009
4           21/05/2009    21/05/2009
5           25/05/2009    NULL
6           01/06/2009    03/06/2009

事件有开始和结束日期(时间无关紧要),NULL endDate表示事件仍在进行中。

我想确定的是两个任意日期之间的日期范围,其中a)没有事件,b)事件重叠。

因此,对于输入日期范围01/04/2009 - 30/06/2009,我希望得到以下结果:

no event: 01/04/2009 - 01/05/2009
overlap : 08/05/2009 - 10/05/2009
overlap : 10/05/2009 - 12/05/2009
no event: 13/05/2009 - 20/05/2009
no event: 22/05/2009 - 24/05/2009
overlap : 01/06/2009 - 03/06/2009

请注意,两个相邻的重叠范围可以作为一个结果。

任何人都可以帮我用SQL算法来生成这个结果集吗?

编辑:目标平台数据库是SQL Server 2005.日期记录为10/05/2009 00:00:00,表示事件在10/5/2009 00:00:00和10之间的某个时间结束/ 5/2009 23:59:59开始日期也是如此。因此输入日期范围也可以读作01/04/2009 00:00:00 - 30/06/2009 23:59:59。

3 个答案:

答案 0 :(得分:4)

SQL Server中展平交叉时间跨度的函数有点变化:

SQL Server中基于光标的方法比基于集合的方法更快,这是极少数情况之一:


CREATE FUNCTION mytable(@p_from DATETIME, @p_till DATETIME)
RETURNS @t TABLE
        (
        q_type VARCHAR(20) NOT NULL,
        q_start DATETIME NOT NULL,
        q_end DATETIME NOT NULL
        )
AS
BEGIN
        DECLARE @qs DATETIME
        DECLARE @qe DATETIME
        DECLARE @ms DATETIME
        DECLARE @me DATETIME
        DECLARE cr_span CURSOR FAST_FORWARD
        FOR
        SELECT  startDate, endDate
        FROM    mytable
        WHERE   startDate BETWEEN @p_from AND @p_till
        ORDER BY
                startDate 
        OPEN    cr_span
        FETCH   NEXT
        FROM    cr_span
        INTO    @qs, @qe
        SET @ms = @qs
        SET @me = @qe
        WHILE @@FETCH_STATUS = 0
        BEGIN
                FETCH   NEXT
                FROM    cr_span
                INTO    @qs, @qe
                IF @qs > @me
                BEGIN
                        INSERT
                        INTO    @t
                        VALUES ('overlap', @ms, @me)
                        INSERT
                        INTO    @t
                        VALUES ('gap', @me, @qs)
                        SET @ms = @qs
                END
                SET @me = CASE WHEN @qe > @me THEN @qe ELSE @me END
        END
        IF @ms IS NOT NULL
        BEGIN
                INSERT
                INTO    @t
                VALUES  (@ms, @me)
        END
        CLOSE   cr_span
        RETURN
END
GO

此函数将每个连续的交叉范围集压缩为一个范围,并返回范围和后续间隙。

答案 1 :(得分:1)

如果没有真正了解您要解决的问题,我的解决方案就是解决某些问题:

  1. 创建表函数(UDF)“all-dates”,它将返回一年中的所有日期。
  2. 通过将事件内部加入到所有日期where the date is between event's start and end dates,将您的事件转换为单独的日期(一个事件行将变为与其中的天数一样多的行)...保留原始eventId。
  3. 使用所有日期(再次)执行事件日期的外部联接以查找间隙或未命中。
  4. where dates are same but eventId is not上自行加入活动日期以查找重叠信息。

答案 2 :(得分:0)

我的穷人,使用postgresql,你可以做到这一点:

(start1,end1)OVERLAPS(start2,end2) (start1,length1)OVERLAPS(start2,length2)

当两个时间段(由其端点定义)重叠时,此表达式产生true,当它们不重叠时为false。端点可以指定为日期,时间或时间戳对;或作为日期,时间或时间戳,后跟间隔。

SELECT(DATE'2001-02-16',DATE'2001-12-21')OVERLAPS        (日期'2001-10-30',日期'2002-10-30'); 结果:是的 SELECT(日期'2001-02-16',INTERVAL'100天')重叠        (日期'2001-10-30',日期'2002-10-30'); 结果:false

但在sql server下,我不知道...... 遗憾