拆分发生在一天边界内的事件
Splitting up events that occur over the day boundary
我有一个 table 事件,有开始时间和结束时间,有些事件的开始时间在午夜之前,结束时间在午夜之后。我想生成在午夜分界点拆分这些事件的输出,以便将它们计入各自的日期。
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-22 07:00:00.000 |
上面的table可以通过查询产生:
SELECT EVENT_ID,
START_TIME,
END_TIME
FROM EVENTS
WHERE START_TIME BETWEEN '2021-02-21 00:00:00.000' AND '2021-02-21 23:59:59.999'
;
我想要的输出将在午夜拆分跨越多天的事件:
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-21 23:59:59.999 |
| 1002 | 2021-02-22 00:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-21 23:59:59.999 |
| 1004 | 2021-02-22 00:00:00.000 | 2021-02-22 07:00:00.000 |
如有任何帮助,我们将不胜感激。理想情况下,我想在没有功能或创建新的 tables.
的情况下生成它
请注意,我使用的是 SQL Server 2016
您可以为此使用递归 CTE:
with cte as (
select event_id, start_time,
(case when datediff(day, start_time, end_time) = 0 then end_time
else dateadd(day, 1, convert(date, start_time))
end) as end_time,
end_time as real_end_time
from t
union all
select event_id, end_time,
(case when dateadd(day, 1, convert(date, end_time)) > real_end_time
then real_end_time
else dateadd(day, 1, convert(date, end_time))
end),
real_end_time
from cte
where end_time < real_end_time
)
select *
from cte;
Here 是一个 db<>fiddle.
使用 table 个数字
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
),nmbs as(
select row_number() over(order by t1.n) - 1 n
from t0 t1 cross join t0 t2 cross join t0 t3
)
select event_id,
case when n = 0
then start_time
else dateadd(day, n, convert(date, start_time))
end start_time,
case when datediff(day, start_time, end_time) = n
then end_time
else dateadd(second, -1, dateadd(day, n + 1, convert(datetime, convert(date, start_time))))
end as end_time
from Events
cross apply (
select top (datediff(day, start_time, end_time) + 1) n
from nmbs) ns
下面的方法解决了START_TIME和END_TIME之间午夜的情况。上面的“期望输出”表示 START_TIME 和 END_TIME 之间只发生了一个午夜。
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t
CREATE TABLE #t ( Event_ID INT, START_TIME DATETIME2, END_TIME DATETIME2)
INSERT INTO #t (Event_ID, START_TIME, END_TIME)
VALUES
( 1001, '2021-02-21 14:00:00.000', '2021-02-21 18:00:00.000' )
, ( 1002, '2021-02-21 17:00:00.000', '2021-02-22 03:00:00.000' )
, ( 1003, '2021-02-21 18:00:00.000', '2021-02-21 22:00:00.000' )
, ( 1004, '2021-02-21 22:00:00.000', '2021-02-22 07:00:00.000' )
-- get original data plus midnight after START_TIME
IF OBJECT_ID('tempdb..#stage') IS NOT NULL DROP TABLE #stage
SELECT *
, CONVERT(DATETIME2, CONVERT(DATE, DATEADD(DAY, 1, t.START_TIME))) d
INTO #stage
FROM #t t
-- get all rows
SELECT Event_ID, START_TIME
, CASE WHEN d > END_TIME THEN END_TIME ELSE d END END_TIME
FROM #stage
UNION ALL
-- get rows where midnight occurs between START_TIME and END_TIME
SELECT Event_ID
, CASE WHEN d > END_TIME THEN START_TIME ELSE d END START_TIME
, END_TIME
FROM #stage
WHERE d < END_TIME
ORDER BY Event_ID
我有一个 table 事件,有开始时间和结束时间,有些事件的开始时间在午夜之前,结束时间在午夜之后。我想生成在午夜分界点拆分这些事件的输出,以便将它们计入各自的日期。
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-22 07:00:00.000 |
上面的table可以通过查询产生:
SELECT EVENT_ID,
START_TIME,
END_TIME
FROM EVENTS
WHERE START_TIME BETWEEN '2021-02-21 00:00:00.000' AND '2021-02-21 23:59:59.999'
;
我想要的输出将在午夜拆分跨越多天的事件:
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-21 23:59:59.999 |
| 1002 | 2021-02-22 00:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-21 23:59:59.999 |
| 1004 | 2021-02-22 00:00:00.000 | 2021-02-22 07:00:00.000 |
如有任何帮助,我们将不胜感激。理想情况下,我想在没有功能或创建新的 tables.
的情况下生成它请注意,我使用的是 SQL Server 2016
您可以为此使用递归 CTE:
with cte as (
select event_id, start_time,
(case when datediff(day, start_time, end_time) = 0 then end_time
else dateadd(day, 1, convert(date, start_time))
end) as end_time,
end_time as real_end_time
from t
union all
select event_id, end_time,
(case when dateadd(day, 1, convert(date, end_time)) > real_end_time
then real_end_time
else dateadd(day, 1, convert(date, end_time))
end),
real_end_time
from cte
where end_time < real_end_time
)
select *
from cte;
Here 是一个 db<>fiddle.
使用 table 个数字
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
),nmbs as(
select row_number() over(order by t1.n) - 1 n
from t0 t1 cross join t0 t2 cross join t0 t3
)
select event_id,
case when n = 0
then start_time
else dateadd(day, n, convert(date, start_time))
end start_time,
case when datediff(day, start_time, end_time) = n
then end_time
else dateadd(second, -1, dateadd(day, n + 1, convert(datetime, convert(date, start_time))))
end as end_time
from Events
cross apply (
select top (datediff(day, start_time, end_time) + 1) n
from nmbs) ns
下面的方法解决了START_TIME和END_TIME之间午夜的情况。上面的“期望输出”表示 START_TIME 和 END_TIME 之间只发生了一个午夜。
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t
CREATE TABLE #t ( Event_ID INT, START_TIME DATETIME2, END_TIME DATETIME2)
INSERT INTO #t (Event_ID, START_TIME, END_TIME)
VALUES
( 1001, '2021-02-21 14:00:00.000', '2021-02-21 18:00:00.000' )
, ( 1002, '2021-02-21 17:00:00.000', '2021-02-22 03:00:00.000' )
, ( 1003, '2021-02-21 18:00:00.000', '2021-02-21 22:00:00.000' )
, ( 1004, '2021-02-21 22:00:00.000', '2021-02-22 07:00:00.000' )
-- get original data plus midnight after START_TIME
IF OBJECT_ID('tempdb..#stage') IS NOT NULL DROP TABLE #stage
SELECT *
, CONVERT(DATETIME2, CONVERT(DATE, DATEADD(DAY, 1, t.START_TIME))) d
INTO #stage
FROM #t t
-- get all rows
SELECT Event_ID, START_TIME
, CASE WHEN d > END_TIME THEN END_TIME ELSE d END END_TIME
FROM #stage
UNION ALL
-- get rows where midnight occurs between START_TIME and END_TIME
SELECT Event_ID
, CASE WHEN d > END_TIME THEN START_TIME ELSE d END START_TIME
, END_TIME
FROM #stage
WHERE d < END_TIME
ORDER BY Event_ID