如何查找缺失的小时数并将数据显示为 0
How to find missing hours and show data as 0
此查询提取每天每个小时的计数,但忽略没有计数的任何日期和时间。如何填写所选日期之间缺失的日期和小时数?
SELECT CAST(RecordTime AS date) AS Date, DATENAME(dw, RecordTime) AS [Day of the week], DATEPART(hour, RecordTime) AS [Hour of the day], COUNT(*) AS [Hourly Count]
FROM Counts
WHERE (RecordTime >= CONVERT(DATETIME, '2022-04-01 00:00:00', 102)) AND (RecordTime < CONVERT(DATETIME, '2022-05-01 00:00:00', 102)) AND (MachineNum = 11) AND (Cavity = 1)
GROUP BY CAST(RecordTime AS date), DATEPART(hour, RecordTime), DATENAME(dw, RecordTime)
ORDER BY Date, [Hour of the day]
您需要一个参考 table 来存储所有日期和相应的时间。它将是一种添加了小时的日历 table。
第 1 步:
创造 table 小时。
CREATE TABLE hours
(
hour_key INTEGER
);
INSERT INTO hours
VALUES (0),
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10),
(11),
(12),
(13),
(14),
(15),
(16),
(17),
(18),
(19),
(20),
(21),
(22),
(23);
第 2 步:
创建一个 table,它将具有所需的日期和时间字段。
SELECT Cast(recordtime AS DATE) AS Date_key,
Datename(dw, recordtime) week_key,
hour_key
FROM hours
CROSS JOIN counts
GROUP BY Cast(recordtime AS DATE),
Datename(dw, recordtime),
hour_key
注意:使用 Counts table 来导出日期和小时字段是一个糟糕的解决方案,因为它可能是事务性的 table 并且具有巨大的记录数。而是使用日历 table.
最后一步:
使用在步骤 2 中创建的 table 作为主要 table(或用作子查询)和 left join
Counts
基于日期和时间。
下面的查询应该会给你想要的输出。
SELECT date_key AS Date,
week_key AS [Day of the week],
hour_key AS [Hour of the day],
Count(c.recordtime) AS [Hourly Count]
FROM (SELECT Cast(recordtime AS DATE) AS Date_key,
Datename(dw, recordtime) week_key,
hour_key
FROM hours
CROSS JOIN counts
GROUP BY Cast(recordtime AS DATE),
Datename(dw, recordtime),
hour_key)cal
LEFT JOIN counts c
ON cal.date_key = Cast(c.recordtime AS DATE)
AND cal.hour_key = Datepart(hour, c.recordtime)
GROUP BY date_key,
week_key,
hour_key
ORDER BY date,
[hour of the day]
SQL Fiddle: Try it here
正如 Larnu 所建议的,您需要生成一个包含范围内所有天+小时组合的完整数据集,以便左连接到。根据我的计算,您需要 30 天 * 24 小时 = 720 行。如果您还没有 a numbers table, or a calendar table, or a sequence generating function,您可以使用递归 CTE 生成它,如下所示:
DECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH days(d) AS
(
SELECT 0 UNION ALL SELECT d+1 FROM days
WHERE d < DATEDIFF(DAY, @StartDate, @AfterLastDate) - 1
), hours(h) AS
(
SELECT 0 UNION ALL SELECT h+1 FROM hours WHERE h<23
),
dates(DayHour, h) AS
(
SELECT DATEADD(HOUR, hours.h, DATEADD(DAY, days.d, @StartDate)), hours.h
FROM days CROSS JOIN hours
)
SELECT d.DayHour, DATENAME(WEEKDAY, DayHour), d.h
FROM dates AS d
ORDER BY d.DayHour;
输出:
DayHour
Day of the week
Hour of the day
2022-04-01 00:00:00.000
Friday
0
2022-04-01 01:00:00.000
Friday
1
2022-04-01 02:00:00.000
Friday
2
... 714 more rows ...
2022-04-30 21:00:00.000
Saturday
21
2022-04-30 22:00:00.000
Saturday
22
2022-04-30 23:00:00.000
Saturday
23
现在,我们只需要对您现有的 table:
进行左外连接 that
DECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH days(d) AS
(
SELECT 0 UNION ALL SELECT d+1 FROM days
WHERE d < DATEDIFF(DAY, @StartDate, @AfterLastDate) - 1
), hours(h) AS
(
SELECT 0 UNION ALL SELECT h+1 FROM hours WHERE h<23
),
dates(DayHour, h) AS
(
SELECT DATEADD(HOUR, hours.h, DATEADD(DAY, days.d, @StartDate)),
hours.h FROM days CROSS JOIN hours
)
SELECT [Date] = CONVERT(date, d.DayHour),
[Day of the week] = DATENAME(WEEKDAY, d.DayHour),
[Hour of the day] = d.h,
[Hourly Count] = COUNT(c.RecordTime)
FROM dates AS d
LEFT OUTER JOIN dbo.Counts AS c
ON c.RecordTime >= d.DayHour
AND c.RecordTime < DATEADD(HOUR, 1, d.DayHour)
AND c.MachineNum = 11
AND c.Cavity = 1
GROUP BY CONVERT(date, d.DayHour), DATENAME(WEEKDAY, DayHour), d.h
ORDER BY [Date], [Hour of the day];
如果您有数字 table,则生成日期会更容易一些。这是一个简单的示例,仅包含 1,000 行作为您期望的最大日期范围,并使用递归 CTE - 有多种方法可以最初填充数字 table 并且性能并不重要。
CREATE TABLE dbo.Numbers(n int PRIMARY KEY);
;WITH x(x) AS
(
SELECT 0 UNION ALL SELECT x+1 FROM x
WHERE x < 1000
)
INSERT dbo.Numbers(n)
SELECT x FROM x OPTION (MAXRECURSION 0);
现在查询获取范围内的所有日期:
DECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH dates(d) AS
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @AfterLastDate)) n
FROM dbo.Numbers ORDER BY n
),
hours(h) AS
(
SELECT TOP (24) n FROM dbo.Numbers ORDER BY n
)
SELECT DayHour = DATEADD(HOUR, hours.h,
DATEADD(DAY, dates.d, @StartDate))
FROM dates CROSS JOIN hours
ORDER BY DayHour;
然后您可以将其用作核心数据集以左连接,就像上面的示例一样。
此查询提取每天每个小时的计数,但忽略没有计数的任何日期和时间。如何填写所选日期之间缺失的日期和小时数?
SELECT CAST(RecordTime AS date) AS Date, DATENAME(dw, RecordTime) AS [Day of the week], DATEPART(hour, RecordTime) AS [Hour of the day], COUNT(*) AS [Hourly Count]
FROM Counts
WHERE (RecordTime >= CONVERT(DATETIME, '2022-04-01 00:00:00', 102)) AND (RecordTime < CONVERT(DATETIME, '2022-05-01 00:00:00', 102)) AND (MachineNum = 11) AND (Cavity = 1)
GROUP BY CAST(RecordTime AS date), DATEPART(hour, RecordTime), DATENAME(dw, RecordTime)
ORDER BY Date, [Hour of the day]
您需要一个参考 table 来存储所有日期和相应的时间。它将是一种添加了小时的日历 table。
第 1 步:
创造 table 小时。
CREATE TABLE hours
(
hour_key INTEGER
);
INSERT INTO hours
VALUES (0),
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10),
(11),
(12),
(13),
(14),
(15),
(16),
(17),
(18),
(19),
(20),
(21),
(22),
(23);
第 2 步:
创建一个 table,它将具有所需的日期和时间字段。
SELECT Cast(recordtime AS DATE) AS Date_key,
Datename(dw, recordtime) week_key,
hour_key
FROM hours
CROSS JOIN counts
GROUP BY Cast(recordtime AS DATE),
Datename(dw, recordtime),
hour_key
注意:使用 Counts table 来导出日期和小时字段是一个糟糕的解决方案,因为它可能是事务性的 table 并且具有巨大的记录数。而是使用日历 table.
最后一步:
使用在步骤 2 中创建的 table 作为主要 table(或用作子查询)和 left join
Counts
基于日期和时间。
下面的查询应该会给你想要的输出。
SELECT date_key AS Date,
week_key AS [Day of the week],
hour_key AS [Hour of the day],
Count(c.recordtime) AS [Hourly Count]
FROM (SELECT Cast(recordtime AS DATE) AS Date_key,
Datename(dw, recordtime) week_key,
hour_key
FROM hours
CROSS JOIN counts
GROUP BY Cast(recordtime AS DATE),
Datename(dw, recordtime),
hour_key)cal
LEFT JOIN counts c
ON cal.date_key = Cast(c.recordtime AS DATE)
AND cal.hour_key = Datepart(hour, c.recordtime)
GROUP BY date_key,
week_key,
hour_key
ORDER BY date,
[hour of the day]
SQL Fiddle: Try it here
正如 Larnu 所建议的,您需要生成一个包含范围内所有天+小时组合的完整数据集,以便左连接到。根据我的计算,您需要 30 天 * 24 小时 = 720 行。如果您还没有 a numbers table, or a calendar table, or a sequence generating function,您可以使用递归 CTE 生成它,如下所示:
DECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH days(d) AS
(
SELECT 0 UNION ALL SELECT d+1 FROM days
WHERE d < DATEDIFF(DAY, @StartDate, @AfterLastDate) - 1
), hours(h) AS
(
SELECT 0 UNION ALL SELECT h+1 FROM hours WHERE h<23
),
dates(DayHour, h) AS
(
SELECT DATEADD(HOUR, hours.h, DATEADD(DAY, days.d, @StartDate)), hours.h
FROM days CROSS JOIN hours
)
SELECT d.DayHour, DATENAME(WEEKDAY, DayHour), d.h
FROM dates AS d
ORDER BY d.DayHour;
输出:
DayHour Day of the week Hour of the day 2022-04-01 00:00:00.000 Friday 0 2022-04-01 01:00:00.000 Friday 1 2022-04-01 02:00:00.000 Friday 2 ... 714 more rows ... 2022-04-30 21:00:00.000 Saturday 21 2022-04-30 22:00:00.000 Saturday 22 2022-04-30 23:00:00.000 Saturday 23
现在,我们只需要对您现有的 table:
进行左外连接 thatDECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH days(d) AS
(
SELECT 0 UNION ALL SELECT d+1 FROM days
WHERE d < DATEDIFF(DAY, @StartDate, @AfterLastDate) - 1
), hours(h) AS
(
SELECT 0 UNION ALL SELECT h+1 FROM hours WHERE h<23
),
dates(DayHour, h) AS
(
SELECT DATEADD(HOUR, hours.h, DATEADD(DAY, days.d, @StartDate)),
hours.h FROM days CROSS JOIN hours
)
SELECT [Date] = CONVERT(date, d.DayHour),
[Day of the week] = DATENAME(WEEKDAY, d.DayHour),
[Hour of the day] = d.h,
[Hourly Count] = COUNT(c.RecordTime)
FROM dates AS d
LEFT OUTER JOIN dbo.Counts AS c
ON c.RecordTime >= d.DayHour
AND c.RecordTime < DATEADD(HOUR, 1, d.DayHour)
AND c.MachineNum = 11
AND c.Cavity = 1
GROUP BY CONVERT(date, d.DayHour), DATENAME(WEEKDAY, DayHour), d.h
ORDER BY [Date], [Hour of the day];
如果您有数字 table,则生成日期会更容易一些。这是一个简单的示例,仅包含 1,000 行作为您期望的最大日期范围,并使用递归 CTE - 有多种方法可以最初填充数字 table 并且性能并不重要。
CREATE TABLE dbo.Numbers(n int PRIMARY KEY);
;WITH x(x) AS
(
SELECT 0 UNION ALL SELECT x+1 FROM x
WHERE x < 1000
)
INSERT dbo.Numbers(n)
SELECT x FROM x OPTION (MAXRECURSION 0);
现在查询获取范围内的所有日期:
DECLARE @StartDate datetime = '20220401',
@AfterLastDate datetime = '20220501';
;WITH dates(d) AS
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @AfterLastDate)) n
FROM dbo.Numbers ORDER BY n
),
hours(h) AS
(
SELECT TOP (24) n FROM dbo.Numbers ORDER BY n
)
SELECT DayHour = DATEADD(HOUR, hours.h,
DATEADD(DAY, dates.d, @StartDate))
FROM dates CROSS JOIN hours
ORDER BY DayHour;
然后您可以将其用作核心数据集以左连接,就像上面的示例一样。