Return 如果不存在则每个时间增量一行

Return a row for each time increment if it does not exist

我有一个查询,returns 一年中每天每 15 分钟增量收到的工作量。问题是,如果在该时间范围内收到作业,则每 15 分钟增量中只有 returns 数据。查询的简洁版本是:

SELECT 
DATEPART(MONTH, Datim) AS Month#,
DATEPART(DAY, Datim) AS Day#,
(DATEPART(HOUR, Datim) + 1) AS Hour#,
((DATEPART(MINUTE, Datim) / 15) + 1) AS Interval#,
COUNT(JobNumber) AS TotalJobs

FROM Table

WHERE (Datim >= '2020-01-01' AND  Datim <= '2021-01-01')

GROUP BY
DATEPART(MONTH, Datim),
DATEPART(DAY, Datim),
(DATEPART(HOUR, Datim) + 1),
((DATEPART(MINUTE, Datim) / 15) + 1)

ORDER BY
DATEPART(MONTH, Datim),
(DATEPART(DAY, Datim) + 0),
(DATEPART(HOUR, Datim) + 1),
((DATEPART(MINUTE, Datim) / 15) + 1)

这是当前返回的输出:

Month# Day# Hour# Interval# TotalJobs
1 1 1 3 123
1 1 2 4 456
1 1 4 1 789

我希望数据输出为每个 15 分钟增量的 TotalJobs 字段填充 0,如果它们不存在,则填充小时。最终输出如下所示:

Month# Day# Hour# Interval# TotalJobs
1 1 1 1 0
1 1 1 2 0
1 1 1 3 123
1 1 1 4 0
1 1 2 1 0
1 1 2 2 0
1 1 2 3 0
1 1 2 4 456
1 1 3 1 0
1 1 3 2 0
1 1 3 3 0
1 1 3 4 0
1 1 4 1 789
1 1 4 2 0
1 1 4 3 0
1 1 4 4 0

关于实现此目标的最佳方法有什么想法吗?

可能的解决方案 tally table.

示例数据

将您当前的查询移动到子查询或公共 table 表达式 (CTE),以便您可以使用它进行左连接。在这里,我只是重新创建了您当前查询的结果行。

create table AvailableData
(
  Month int,
  Day int,
  Hour int,
  Interval int,
  TotalJobs int
);

insert into AvailableData (Month, Day, Hour, Interval, TotalJobs) values
(1, 1, 1, 3, 123),
(1, 1, 2, 4, 456),
(1, 1, 4, 1, 789);

解决方案

为您提供 2021 年的前两个月。更新 CTE 的 TallyIntervals 以获得更多增量数字和更多月份。

with Tally (n) as
(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM       (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n) -- 10^1 = 10
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n) -- 10^2 = 100
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n) -- 10^3 = 1000
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n) -- 10^4 = 10000
),
Intervals as
(
  select top (2*31*24*4) -- increase as required, max determined by Tally, example for 2 months
       --t.n,
       --dateadd(day, (t.n-1)/(24*4), '2021-01-01')                   as [Date],
         datepart(month, dateadd(day,  (t.n-1)/(24*4), '2021-01-01')) as [Month],
         datepart(day,   dateadd(day,  (t.n-1)/(24*4), '2021-01-01')) as [Day],
         (((t.n-1) % (24*4)))/4 +1                                    as [Hour],
         ( (t.n-1) %     4 )    +1                                    as [Interval]
  from Tally t
)
select i.Month,
       i.Day,
       i.Hour,
       i.Interval,
       coalesce(ad.TotalJobs, 0) as TotalJobs
from Intervals i
left join AvailableData ad
  on  ad.Month = i.Month
  and ad.Day = i.Day
  and ad.Hour = i.Hour
  and ad.Interval = i.Interval;

Fiddle 有一些中间结果。


完整解决方案

快速合并和编辑 2021 年全年。未验证。

with Tally (n) as
(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (      VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n) -- 10^1 = 10
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n) -- 10^2 = 100
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n) -- 10^3 = 1000
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n) -- 10^4 = 10000
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e(n) -- 10^5 = 100000
),
Intervals as
(
  select top (365*24*4) -- full year of 2021
         datepart(month, dateadd(day,  (t.n-1)/(24*4), '2021-01-01')) as [Month],
         datepart(day,   dateadd(day,  (t.n-1)/(24*4), '2021-01-01')) as [Day],
         (((t.n-1) % (24*4)))/4 +1                                    as [Hour],
         ( (t.n-1) %     4 )    +1                                    as [Interval]
  from Tally t
),
AvailableData as
(
  SELECT 
  DATEPART(MONTH, Datim) AS [Month],
  DATEPART(DAY, Datim) AS [Day],
  (DATEPART(HOUR, Datim) + 1) AS [Hour],
  ((DATEPART(MINUTE, Datim) / 15) + 1) AS [Interval],
  COUNT(JobNumber) AS TotalJobs
  
  FROM Table
  
  WHERE (Datim >= '2020-01-01' AND  Datim <= '2021-01-01')
  
  GROUP BY
  DATEPART(MONTH, Datim),
  DATEPART(DAY, Datim),
  (DATEPART(HOUR, Datim) + 1),
  ((DATEPART(MINUTE, Datim) / 15) + 1)
)
select i.Month,
       i.Day,
       i.Hour,
       i.Interval,
       coalesce(ad.TotalJobs, 0) as TotalJobs
from Intervals i
left join AvailableData ad
  on  ad.Month = i.Month
  and ad.Day = i.Day
  and ad.Hour = i.Hour
  and ad.Interval = i.Interval
order by i.Month,
         i.Day,
         i.Hour,
         i.Interval;