SQL日期范围查询 - 表比较

sam*_*kin 5 sql t-sql sql-server date gaps-and-islands

我有两个SQL Server表包含以下信息:

t_venues:

venue_id 是独特的

venue_id  |  start_date  |  end_date
       1  |  01/01/2014  |  02/01/2014
       2  |  05/01/2014  |  05/01/2014
       3  |  09/01/2014  |  15/01/2014
       4  |  20/01/2014  |  30/01/2014
Run Code Online (Sandbox Code Playgroud)

t_venueuser:

venue_id 不是唯一的

venue_id  |  start_date  |  end_date
       1  |  02/01/2014  |  02/01/2014
       2  |  05/01/2014  |  05/01/2014
       3  |  09/01/2014  |  10/01/2014
       4  |  23/01/2014  |  25/01/2014
Run Code Online (Sandbox Code Playgroud)

从这两个表中我需要找到每个范围未选择的日期,因此输出如下所示:

venue_id  |  start_date  |  end_date
       1  |  01/01/2014  |  01/01/2014
       3  |  11/01/2014  |  15/01/2014
       4  |  20/01/2014  |  22/01/2014
       4  |  26/01/2014  |  30/01/2014
Run Code Online (Sandbox Code Playgroud)

我可以比较这两个表,并t_venues使用'except' 获取日期范围以显示在我的查询中,但我无法获得查询以生成未选择的日期.任何帮助,将不胜感激.

Mac*_*ack 1

这是一个完整的黑客,但它给出了您需要的结果,我只根据您提供的数据对其进行了测试,因此较大的集合很可能会出现问题。

一般来说,您在这里要解决的是间隙和岛屿问题的变体,这是(简单地)一个缺少某些项目的序列。缺失的项目称为间隙,现有的项目称为岛屿。如果您想总体了解这个问题,请查看以下几篇文章:

代码:

;with dates as
(
    SELECT  vdates.venue_id,    
            vdates.vdate
    FROM  ( SELECT DATEADD(d,sv.number,v.start_date) vdate
                 , v.venue_id
            FROM t_venues v
            INNER JOIN master..spt_values sv 
                ON sv.type='P'
               AND sv.number BETWEEN 0 AND datediff(d, v.start_date, v.end_date)) vdates
    LEFT JOIN t_venueuser vu
        ON vdates.vdate >= vu.start_date
       AND vdates.vdate <= vu.end_date
       AND vdates.venue_id = vu.venue_id
    WHERE ISNULL(vu.venue_id,-1) = -1
)
SELECT venue_id, ISNULL([1],[2]) StartDate, [2] EndDate
FROM   (SELECT venue_id, rDate, ROW_NUMBER() OVER (PARTITION BY venue_id, DateType ORDER BY rDate) AS rType, DateType as dType
        FROM(   SELECT d1.venue_id
                      ,d1.vdate AS rDate
                      ,'1' AS DateType
                FROM dates AS d1    
                LEFT JOIN dates AS d0
                    ON DATEADD(d,-1,d1.vdate) = d0.vdate
                LEFT JOIN dates AS d2       
                    ON DATEADD(d,1,d1.vdate) = d2.vdate
                WHERE CASE ISNULL(d2.vdate, '01 Jan 1753') WHEN '01 Jan 1753' THEN '2' ELSE '1' END = 1
                AND ISNULL(d0.vdate, '01 Jan 1753') = '01 Jan 1753'
                UNION 
                SELECT d1.venue_id
                      ,ISNULL(d2.vdate,d1.vdate)
                      ,'2'
                FROM dates AS d1    
                LEFT JOIN dates AS d2       
                    ON DATEADD(d,1,d1.vdate) = d2.vdate
                WHERE CASE ISNULL(d2.vdate, '01 Jan 1753') WHEN '01 Jan 1753' THEN '2' ELSE '1' END = 2
            ) res
        ) src
PIVOT   (MIN (rDate)
        FOR dType IN
        ( [1], [2] )
        ) AS pvt
Run Code Online (Sandbox Code Playgroud)

结果:

venue_id    StartDate   EndDate
1           2014-01-01  2014-01-01
3           2014-01-11  2014-01-15
4           2014-01-20  2014-01-22
4           2014-01-26  2014-01-30
Run Code Online (Sandbox Code Playgroud)