1

我有中级 SQL 技能,但这是我尝试过的最复杂的查询。

我的目标是构建一个查询,显示任何一天中有多少分钟,一组 6 个驱动器正在使用或空闲。“正在使用”的驱动器正在将备份写入磁带,也就是运行作业。驱动器一次只能处理工作。驱动器可以在同一天开始和结束一项工作,或者如果它是一项大工作,则可以在一天开始并在 2 天后结束。最重要的是,我能够报告每个驱动器处于 UP 或 IDLE 的分钟数(两者都很重要),并且即使工作进行到下一天,也只报告它在相应一天工作的分钟数。

因此,复杂性源于以下

  1. 我不能只从结束时间中减去开始时间,然后对特定驱动器运行的所有作业的经过时间求和,因为许多作业跨越午夜,我必须将工作分钟数分配给它们发生的那一天。IE。我不能报告驱动器在 24 小时内完成了 50 小时的工作,仅仅因为工作的结束时间是 2 天。

  2. 开始时间和结束时间列采用 UTC 时间,必须转换为 PST。

  3. 当任何一个驱动器空闲时,我需要占位符,以便我可以显示每个驱动器的启动/空闲时间。

我需要放在一起的表只有两个:

  1. 时间日历表。从 2009 年 10 月 10 日到 2021 年 7 月 10 日,一天中的每一分钟都有一行。

  2. 包含所有已完成作业的开始和结束时间、运行它们的驱动器名称以及作业名称的表。

这是DDL一个日历表,其中包含从 2009 年到 2014 年一天中每一分钟的一行。

WITH e1(min) AS(
    SELECT * FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))x(n)
),
e2(min) AS(
    SELECT e1.min FROM e1, e1 x
),
e4(min) AS(
    SELECT e2.min FROM e2, e2 x
),
e8(min) AS(
    SELECT e4.min FROM e4, e4 x
),
cteTally(min) AS(
    SELECT TOP 6307204 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
    FROM e8
),
Test(min) AS(
SELECT DATEADD( minute, min, DATEADD( YEAR, -2, GETDATE()))
FROM cteTally)
SELECT DATEADD( MINUTE, DATEDIFF( MINUTE, 0, DATEADD( YEAR, -2, GETDATE())), 0)
FROM Test
WHERE min <= DATEADD( YEAR, 10, GETDATE())

这是包含设备/作业/开始和结束时间的表的示例 DDL。

CREATE TABLE JobHistorySummary
(JobName nvarchar(255),
ActualStartTime datetime,
EndTime datetime,
DeviceName nvarchar(128))
INSERT INTO JobHistorySummary
VALUES
('FOAMTools E: Weekly - FULL', '2013-08-04 03:20:00.000', '2013-08-04 20:20:00.000', '1 Drv'),
('HRDuplex D: Weekly - FULL', '2013-08-04 18:26:00.000', '2013-08-05 13:00:00.000', '2 Drv'),
('HRDuplex D: Daily - INC', '2013-08-04 20:44:00.000', '2013-08-05 15:50:00.000', '1 Drv'),
('PayNROLL C: Weekly - FULL', '2013-08-04 00:00:00.000', '2013-08-06 15:40:00.000','3 Drv'),
('PayNROLL C: Daily - INC', '2013-08-05 06:30:00.000', '2013-08-05 06:50:00.000', '4 Drv'),
('SmallIBM F: Daily - FULL', '2013-08-04 00:30:00.000', '2013-08-04 06:30:00.000', '5 Drv'),
('BigIBM F: Daily - INC', '2013-08-06 12:30:00.000', '2013-08-06 12:50:00.000', '6 Drv');

需要获取本地时间的计算是 [ActualStartTime]+ GETDATE() - GETUTCDATE())

即使我只需要两个表,我也无法弄清楚加入它们的逻辑,以便它们为驱动器空闲的那些日期时间创建 NULL 占位符。我想将具有 NULL 值的行计数为每个驱动器的空闲分钟数。此外,我无法弄清楚如何将使用分钟数与它们发生的那一天隔离开来……这意味着每个驱动器每天的工作时间不超过 1440 分钟,即使对于跨越午夜的工作也是如此。次日的分钟数被分配为由各自的驱动器工作到次日的分钟数。

4

1 回答 1

0

下面显示了如何生成每个设备每天的使用时间。空闲时间仅为 24 减去该时间。假设您已经生成了一个表,其中包含表范围内的所有日期,让我们用一个 Date 类型的字段 dt 调用该 cal。(容易做到)。下面给出一般方法。

select devicename, cal.dt, sum(time(least(actualendtime, cal.dt)- time(actualstarttime))
from  JobHistorySummary jhs inner join cal 
on (jhs.actualstarttime >= cal.dt and jhs.actualendtime < cal.dt)
group by devicename, cal.dt

现在您已经使用上面的相同语句使用转换后的时间,并且还假设 cal 在转换后的时区中。

select devicename, cal.dt, sum(time(least(convert_tz(actualendtime,'UTC','US/Pacific'), cal.dt)- time(convert_tz(actualstarttime,'UTC','US/Pacific')))
from  JobHistorySummary jhs inner join cal 
on (convert_tz(actualstarttime,'UTC','US/Pacific') >= cal.dt and convert_tz(actualendtime,'UTC','US/Pacific') < cal.dt)
group by devicename, cal.dt

但上述内容也不完全正确,因为 mysql 不会正确地对时间值进行减法和聚合求和。所以你需要使用更多类似的东西:

select devicename, cal.dt, econd_to_time(sum(time_to_second(timediff(time(least(actualendtime, cal.dt), time(actualstarttime)))))
from  JobHistorySummary jhs inner join cal 
on (jhs.actualstarttime >= cal.dt and jhs.actualendtime < cal.dt)
group by devicename, cal.dt
于 2014-12-22T13:21:59.873 回答