计算 T-SQL 中两个日期范围之间的交叉点数
Posted
技术标签:
【中文标题】计算 T-SQL 中两个日期范围之间的交叉点数【英文标题】:Count Number of Intersections Between Two Date Ranges in T-SQL 【发布时间】:2016-07-13 12:12:16 【问题描述】:我有以下两个表(显示了一些示例数据):
节假日
Start | End
-----------|-----------
2000-01-01 | 2000-01-02
2000-02-20 | 2000-02-20
活动
Title | Date
-----------|-----------
Foo | 2000-01-03
Bar | 2000-01-20
如何返回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
【问题讨论】:
你能展示一个你想要的样本输出吗? 上周是在 2000 年 2 月 31 日之后.... 查看更新的问题。 2000-02-31 是一个有效的日期吗?这些 Start 和 End 列是什么类型的? 但是没有 2 月 31 日这样的日期。当您尝试设置此类日期时,它将返回一个超出范围的异常。 【参考方案1】:样本数据
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');
查询
假设 HolidayStart
和 HolidayEnd
日期都包含在内。
CROSS APPLY E
只是为DATEADD
函数的结果创建方便的别名,这样我以后可以写短的EventStart
而不是长的DATEADD
表达式。
OUTER APPLY
给出Holiday
中与给定事件的周相交的所有行的列表。交叉点持续时间从max of starts
到 min 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
【讨论】:
【参考方案2】:尝试以下查询。
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
【讨论】:
【参考方案3】:此查询还管理事件日期属于假日期间的情况。
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
【讨论】:
以上是关于计算 T-SQL 中两个日期范围之间的交叉点数的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server - where + TVF/SVF、交叉应用、T-SQL