【问题标题】:SQL Server take a time duration and parse the time into the hour parts of that durationSQL Server 需要一个持续时间并将时间解析为该持续时间的小时部分
【发布时间】:2021-05-14 14:20:45
【问题描述】:

我再次发布this question,因为项目发生了变化,并且之前的答案没有返回预期的结果。救护车和消防车有紧急情况发生时的调度时间和宣布紧急情况结束时的结束时间。

活动 1 于 2021 年 5 月 1 日 10:17:33 开始,并于 2021 年 5 月 1 日 10:33:41 结束。

活动 2 于 2021 年 5 月 1 日 11:50:52 开始,并于 2021 年 5 月 1 日 13:18:21 结束。

我想解析从开始到结束的时间量,并在它发生时将其放入小时部分。例如;活动 1 开始于 10:17,结束于 10:33。它将在当天的 10:00 小时部分放置 16 分钟。事件 2 将在 11:00 小时部分放置 10 分钟,在 12:00 小时部分放置 60 分钟,在 13:00 小时部分放置 18 分钟。将分钟放在事件发生的小时数中。

结果应如下所示。虽然我很灵活。例如,如果卡车的名称不能在结果中返回,那也没关系,因为如果 EventID 在那里,我可以关联回原始表。

EventID Ambulance EventDayOfYear EventHour MinutesAllocated
1 Medic10 121 10 16
1 Medic10 121 11 10
2 Ladder73 121 11 10
2 Ladder73 121 12 60
2 Ladder73 121 13 18
3 Engine41 121 13 33
3 Engine41 121 14 21
4 Medic83 121 15 32
4 Medic83 121 16 5
5 Rescue32 121 16 33
6 Medic09 121 23 16
6 Medic09 122 0 39
7 Engine18 121 23 28
7 Engine18 122 0 60
7 Engine18 122 1 34
8 Rescue63 122 0 35

以下 SQL 代码接近于提供正确的结果。 但不重叠天数。有许多紧急事件从 2300 时开始,一直持续到第二天 0300 时。

DECLARE @tempFireEvents TABLE
(
  EventID INT NOT NULL
, Apparatus VARCHAR(10) NOT NULL
, StartDateTime DATETIME NOT NULL
, EndDateTime DATETIME NOT NULL
, DurationInSeconds INT NOT NULL
)

INSERT INTO @tempFireEvents
VALUES
  (1, 'Medic10', 'may 1, 2021 10:17:33', 'may 1, 2021 10:33:41', 968) /*This event is entirely within 1000 hours*/
, (2, 'Ladder73', 'may 1, 2021 11:50:52', 'may 1, 2021 13:18:21', 5249) /*This event spans 1100, 1200 and 1300 hours*/
, (3, 'Engine41', 'may 1, 2021 13:27:17', 'may 1, 2021 14:21:18', 3241) /*This event overlaps 1300 and 1400 hours*/
, (4, 'Medic83', 'may 1, 2021 15:28:08', 'may 1, 2021 16:05:48', 2260) /*This event overlaps 1500 and 1600 hours*/
, (5, 'Rescue32', 'may 1, 2021 16:20:43', 'may 1, 2021 16:53:28', 1965) /*This event is entirely within the 1600 hour part*/
, (6, 'Medic09', 'may 1, 2021 23:44:06', 'may 2, 2021 00:39:52', 3346) /*Notice this overlaps the 2300 and 0000 hours into the following day*/
, (7, 'Engine18', 'may 1, 2021 23:32:58', 'may 2, 2021 01:34:13', 7275) /*Notice this overlaps the 2300, 0000 and 0100 hours into the following day*/
, (8, 'Rescue63', 'may 2, 2021 00:17:45', 'may 2, 2021 00:52:09', 2064) /*Notice this is the 00 hour of the day and does not show in the results*/
;

WITH AllHours AS
(
SELECT 1 AS hourInt
UNION ALL
SELECT hourInt + 1
FROM AllHours
WHERE hourInt < 23
)
,
Combined AS
(
SELECT 
  T.EventID
, H.hourInt
, CASE WHEN DATEPART(HOUR, T.StartDateTime) = H.hourInt THEN 1 ELSE 0 END AS isStart
, CASE WHEN DATEPART(HOUR, T.EndDateTime) = H.hourInt THEN 1 ELSE 0 END AS isEnd
, T.StartDateTime
, T.EndDateTime
FROM @tempFireEvents AS [T]
JOIN AllHours AS [H] ON H.hourInt BETWEEN DATEPART(HOUR, T.StartDateTime) AND DATEPART(HOUR,T.EndDateTime)
)

SELECT 
  EventID
, hourInt
, CASE WHEN isStart = 1 AND isEnd = 0 THEN 60 - DATEPART(MINUTE, StartDateTime)
  WHEN isStart = 0 AND isEnd = 1 THEN DATEPART(MINUTE, EndDateTime)
  WHEN isStart = 1 AND isEnd = 1 THEN DATEPART(MINUTE, EndDateTime) -  DATEPART(MINUTE, StartDateTime)
  ELSE 60
  END AS MinutesInThisHour
FROM Combined
ORDER BY EventID ASC, hourint ASC
;

我怀疑 SQL Server 可能不是实现目标的最佳方法。它可能需要用 Python 编写,带有增量和减量以及计数器。

如果有帮助,我有一个看起来像这样的日历表:

May 1, 2021 00:00:00
May 1, 2021 01:00:00
May 1, 2021 02:00:00
May 1, 2021 03:00:00
May 1, 2021 04:00:00
May 1, 2021 05:00:00
May 1, 2021 06:00:00

日历表对解决这个问题有用吗?

【问题讨论】:

  • 请不要使用 HTML 来回答您的问题。 &lt;body&gt; 标签之类的东西破坏了降价。
  • 您的结束声明“我有一个如下所示的日历表:” 意味着您的日历表将日期存储为varchar。如果是这样,那么恐怕那个日历表就没有用了。有日期和时间数据类型是有原因的。
  • 当我写这个问题时,Stack Overflow 有一条错误消息:它不会在没有正文标签的情况下发布。当我放入 标记时,错误消息消失了。其次,日期时间值作为 DATETIME 存储在 SQL Server 中。
  • AllHours 不应该返回 0..23 吗?另外,为什么是递归 CTE?为什么不with AllHours as (select * from (values (0), (1), ... , (23)) H(HourInt) ) 甚至是理货表?
  • AlwaysLearning:您提出的解决方案不能解决事件 6 和 7 重叠天的问题。事件 6 从 2300 时开始,在 00:00 时结束。事件 7 从 2300 开始,一直持续到 00:00,并在 01:00 结束。我要解决的问题是如何编写代码来处理重叠天数的事件。谢谢。

标签: sql-server python-3.x temporal


【解决方案1】:

CREATE TABLE tempFireEvents
(
EventID VARCHAR(8) NOT NULL,
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
)

INSERT INTO tempFireEvents
VALUES
('fire0001', 'november 1, 2018 10:45:00', 'november 2, 2018 11:30:00'),
('fire0002', 'november 1, 2018 11:50:00', 'november 1, 2018 13:10:00'),
('fire0003', 'november 1, 2018 13:20:00', 'november 1, 2018 14:20:00'),
('fire0004', 'november 1, 2018 15:25:00', 'november 1, 2018 16:05:00'),
('fire0005', 'november 1, 2018 16:20:00', 'november 2, 2018 17:00:00'),
('fire0006', 'november 1, 2018 16:20:00', 'november 1, 2018 17:01:00');

select e.*, hr.ld, 
   60 - case when e.startdatetime > hr.ld then datepart(minute, e.startdatetime) else 0 end
   + case when e.enddatetime < hr.ud then datepart(minute, e.enddatetime)-60 else 0 end as allocatedminutes
from tempFireEvents as e
cross apply
(
  select
    dateadd(hour, datepart(hour,e.startdatetime)+t.rn-1, cast(cast(e.startdatetime as date) as datetime)) as ld, 
    dateadd(hour, datepart(hour,e.startdatetime)+t.rn, cast(cast(e.startdatetime as date) as datetime)) as ud,
    rn
  from
  (
    -- a tally, max 100 rows .. max 100 hours duration
    select top (1+datediff(hour,e.startdatetime,dateadd(minute, -1, e.enddatetime))) row_number() over(order by @@spid) as rn
    from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as a(n)
    cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as b(n)
  ) as t
) as hr;

【讨论】:

  • 非常感谢您的这篇深思熟虑的帖子。您发布的 SQL 代码回答了我的问题,并将活动的分钟数放入每天的时间段中。它还处理日历上与日期重叠的事件。但是,有一个问题会阻止使用此代码。有一行代码:FROM (VALUES(1), (1), (1), (1), (1), etc. 这限制了可以返回的观察数。我有大约 9000 个事件要处理已处理。如何调整此代码以向上扩展?
  • ..@DavidFortMyers,FROM (VALUES..) 部分是“执行的”/应用每个事件/每个事件行。最大 100 行限制适用于事件的持续时间 (StartDatetime-EndDatetime):如果事件超过 100 小时,代码将返回前 100 个“小时箱”,并将“错过”其余的持续时间。 ..如果事件不能超过 100 小时,那么对于任何数量的事件都可以正常工作。您可以扩展代码(每个事件最多 1000 小时等)或使用计数功能或计数表,这将适用于任何最大持续时间/小时...
  • ...@DavidFortMyers ...如果您想进一步优化:代码按原样执行每小时 bin 的计算和比较:60 - case + case ...但是如果事件涵盖更多超过两个小时...那么实际上仅在第一个小时 (rn=1) 和最后一小时 (rn=datediff(Start - [End-1minute]) 需要比较/算术。所有其他中间时间将默认为到 60 分钟。例如,活动从 13:XX 开始,到 18:YY 结束:rn.1 = 60-XX 分钟 | rn.6=YY 分钟,rn.2&rn.3&rn.4&rn.4 = 60 分钟(那些不需要实际计算..)
  • 感谢您的所有帮助。您的专业协助使这个项目变得更加容易。我修改了您的代码以使用我们的 SQL Server,它运行良好。在此之前,我有 300 行 SQL 代码和大约 20 个 CASE 语句。这很难理解并且容易出错。您的代码大大简化了一切。此外,使用 SQL 查询返回的数据更易于进行统计分析。我可以查看每个小时和一周中每一天的总小时数。该输出可以进行统计分析,并具有可视化效果。谢谢你帮助我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
相关资源
最近更新 更多