计算 T-SQL 中两个日期范围之间的交集数

Count Number of Intersections Between Two Date Ranges in T-SQL

我有以下两个表(显示了一些示例数据):

假期

Start      | End
-----------|-----------
2000-01-01 | 2000-01-02
2000-02-20 | 2000-02-20

事件

Title      | Date
-----------|-----------
Foo        | 2000-01-03
Bar        | 2000-01-20

如何 return 在 Event.Date 之前的一周内发生的具有假期天数的所有事件?

SELECT 
    e.Title,
    e.Date,
    DaysHolidayInPastWeek  <-- How to get this?
FROM Event e

示例输出

Title      | Date       | DaysHolidayInPastWeek
-----------|------------|----------------------
Foo        | 2000-01-03 | 2
Bar        | 2000-01-20 | 0

尝试以下查询。

SELECT 
    e.Title,
    e.Date,
    (   
        SELECT
            SUM(DATEDIFF(DAY, h.start, h.end)) AS CountOfHoliday
        FROM
            Holiday h
        WHERE
            h.EventId = e.Id AND -- releation id
            h.Start >= DATEADD(DAY, -7, e.date) AND
            h.Start <= e.date -- Or delete this. Just h.Start >= DATEADD(DAY, -7, e.date)

    ) AS DaysHolidayInPastWeek 
FROM 
    Event e

示例数据

DECLARE @Holiday TABLE (HolidayStart date, HolidayEnd date);
INSERT INTO @Holiday (HolidayStart, HolidayEnd) VALUES
('2000-01-01', '2000-01-02'),
('2000-03-31', '2000-03-31'),
('2000-03-20', '2000-03-27'),
('2000-05-01', '2000-05-30');

DECLARE @Event TABLE (Title nvarchar(50), dt date);
INSERT INTO @Event (Title, dt) VALUES
('Foo', '2000-01-03'),
('Bar', '2000-01-20'),
('444', '2000-04-01'),
('555', '2000-05-10');

查询

假设 HolidayStartHolidayEnd 日期都包含在内。 CROSS APPLY E 只是为 DATEADD 函数的结果创建方便的别名,这样我以后就可以写短的 EventStart 而不是长的 DATEADD 表达式。

OUTER APPLY 给出了 Holiday 中与给定事件的周相交的所有行的列表。交叉路口持续时间从 max of startsmin of ends

Main SELECT 将所有交叉点分组并求和。

SELECT
    Ev.Title
    ,Ev.dt
    ,ISNULL(SUM(DATEDIFF(day, 
        Intersections.IntersectionStart,
        Intersections.IntersectionEnd) + 1), 0) AS DaysHolidayInPastWeek
FROM
    @Event AS Ev
    CROSS APPLY
    (
        SELECT 
            DATEADD(day, -6, Ev.dt) AS EventStart
            ,Ev.dt AS EventEnd
    ) AS E
    OUTER APPLY
    (
        SELECT
            -- intersection duration is:
            -- max of starts
            -- min of ends
            CASE WHEN E.EventStart > H.HolidayStart 
                THEN E.EventStart ELSE H.HolidayStart END AS IntersectionStart
            ,CASE WHEN E.EventEnd < H.HolidayEnd
                THEN E.EventEnd ELSE H.HolidayEnd END AS IntersectionEnd
        FROM @Holiday AS H
        WHERE
            -- two intervals intersect
            H.HolidayEnd >= E.EventStart
            AND H.HolidayStart <= E.EventEnd
    ) AS Intersections
GROUP BY
    Ev.Title
    ,Ev.dt
;

结果

Title       dt  DaysHolidayInPastWeek
Foo 2000-01-03  2
Bar 2000-01-20  0
444 2000-04-01  3
555 2000-05-10  7

此查询还管理事件日期属于假期的情况。

 SELECT Title, Date,
 (
  SELECT        
  sum(cntdays) from
  (select start, [end], 
    case 
      when E.Date between start and [End]   
            then DATEDIFF(DAY, Start, E.Date)

      when ( 
             DATEADD(DAY, -7,E.Date) between start and [End]    
             or 
             DATEADD(DAY, -7,E.Date) < start
           ) and E.Date > start

           then DATEDIFF(DAY, start, [end]) + 1

      else 0
    end as cntdays 
   from Holiday
  ) as H
 ) AS DaysHolidayInPastWeek
 FROM Event AS E