【问题标题】:Return a row for each time increment if it does not exist如果不存在,则为每个时间增量返回一行
【发布时间】:2021-01-18 19:00:16
【问题描述】:

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

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

我希望数据输出为 TotalJobs 字段中的每个 15 分钟增量以及小时(如果它们不存在)填充 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 加入以获得增量号码。请注意,仅 1 月就会为您提供 31 days * 24 hours * 4 intervals = 2976 rows,这可能不是 对用户最友好的概述 :-)
  • 谢谢@Sander!我会研究这种方法。我正在寻找的预期输出将有 12 months * 31 days * 24 hours * 4 intervals = 35715 rows

标签: sql-server tsql datepart


【解决方案1】:

tally 表的可能解决方案。

样本数据

将当前查询移至子查询或公用表表达式 (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;

【讨论】:

  • 很高兴在这里看到 Itzik 的计数功能,而不是愚蠢的 rCTE。他有一个new article。赞成
  • 再次感谢@Sander!这个查询看起来非常接近我想要的,但它似乎没有正确地增加月份和日期。这是间隔部分的问题吗?
  • 你是对的,31* 不应该存在这个因素(我从一月份的 31 天开始)。答案和小提琴已更新。 “完全合并”版本现在使用因子365 来达到一整年。这应该产生 35040 行 (365 days * 24 hours * 4 intervals)。
  • 完美!我真的很感谢这个@Sander 的所有帮助!我只是想快速说明一下,我想要 2020 年的结果,这是闰年,所以我保留了 select top (12*31*24*4) 以容纳额外的一天。
猜你喜欢
  • 2018-04-10
  • 1970-01-01
  • 2020-04-17
  • 1970-01-01
  • 2019-02-15
  • 2012-04-20
  • 2019-11-12
  • 1970-01-01
  • 2013-02-18
相关资源
最近更新 更多