问题陈述: 我在消防部门工作,正在对我的数据进行统计分析。一个问题是生成一个日历年中每天每小时的服务呼叫数量。我需要一张可以连接到一年中每一天和每天每小时的火灾事件的表。我希望的是以下内容(使用军事时间)
2017 年 1 月 1 日 00:00:00
2017 年 1 月 1 日 00:00:00
2017 年 1 月 1 日 01:00:00
2017 年 1 月 1 日 02:00:00
2017 年 1 月 1 日 03:00:00
2017 年 1 月 1 日 04:00:00
2017 年 1 月 1 日 05:00:00
2017 年 1 月 1 日 06:00:00
2017 年 1 月 1 日 07:00:00
2017 年 1 月 1 日 08:00:00
等等直到年底
2017年12月31日21:00:00
2017年12月31日22:00:00
2017年12月31日23:00:00
年底
此表将允许我加入火灾事故表,我将能够统计计算一天中每一小时和一年中每一天的事故数量。计算表是必要的,因为火灾事故表中存在空白。例如; 1 月 1 日 0100 时、0200 时和 0300 时,没有紧急电话打进来。因此,我无法使用火灾事故表进行计算,因为没有没有电话打入时的数据。有间隙的火灾事故表如下所示:
时间间隔、事件地址
2017 年 1 月 1 日 00:00:00, 榆树街 123 号
2017 年 1 月 1 日 04:00:00,橡树街 456 号
2017 年 1 月 1 日 05:00:00,枫树街 789 号
(请注意,0100、0200 和 0300 没有火警电话。这些是时间间隔。) 因为数据中存在间隙,其中零应该是泊松分布所需的计算平均值缺失。平均值不正确。
所需输出: 我的目标是拥有一个包含一天中的小时表的日历,以加入我的火灾事件,以便我的结果集返回。这是一个查询的草稿,如果存在匹配值,则返回日历表中的每一行和火灾事件表中的行。
SELECT
TimeInterval
, COUNT(Incidents) AS [CountOfIncidents] /*this should probably be a COALESCE statement*/
FROM CalendarTable /*all rows from the calendar with hours and rows with data from FireIncidents*/
LEFT OUTER JOIN FireIncidents ON CalendarTable.timeInterval = FireIncidents.TimeInterval
GROUP BY TimeInterval
查询将返回我希望实现的目标:
时间间隔,事件计数
2017 年 1 月 1 日 00:00:00, 5
2017 年 1 月 1 日 01:00:00, 0
2017 年 1 月 1 日 02:00:00, 0
2017 年 1 月 1 日 03:00:00, 0
2017 年 1 月 1 日 04:00:00, 2
2017 年 1 月 1 日 05:00:00, 1
(请注意,0100、0200 和 0300 的时间有零次呼叫。这就是我想要的!现在我可以创建一个直方图,显示有多少小时有零次呼叫。或者我可以计算考虑到零次呼叫的平均值一天中的部分时间。)
我尝试过的: 我已经尝试了以下方法,但我无法弄清楚如何从中创建表格以及如何使其成为成品,正如您在下面的问题段落中看到的那样。
DECLARE @DayOfYearNumber INT
DECLARE @HourNumber INT
SET @DayOfYearNumber = 1
SET @HourNumber = 0
PRINT 'Year' + ', ' + 'CalendarDayOfYear' + ', ' + 'HourOfDay'
WHILE @DayOfYearNumber < 366
BEGIN
SET @HourNumber = 0
WHILE @HourNumber < 24
BEGIN PRINT '2017' + ', ' + CONVERT(VARCHAR, @DayOfYearNumber) + ' ' + CONVERT(VARCHAR, @HourNumber)
SET @HourNumber = @HourNumber + 1
END
SET @DayOfYearNumber = @DayOfYearNumber + 1
END
问题:
如何在 SQL Server 2012 中生成包含一年中的每一天和每天的每个小时的日历表。又是我的例子
2017 年 1 月 1 日 00:00:00
2017 年 1 月 1 日 01:00:00
2017 年 1 月 1 日 02:00:00
2017 年 1 月 1 日 03:00:00
2017 年 1 月 1 日 04:00:00
2017 年 1 月 1 日 05:00:00
2017 年 1 月 1 日 06:00:00
2017 年 1 月 1 日 07:00:00
2017 年 1 月 1 日 08:00:00
等等直到年底
2017年12月31日21:00:00
2017年12月31日22:00:00
2017年12月31日23:00:00
年底
一个简单的方法使用递归:
with d as (
select cast('2017-01-01' as datetime) as dte
union all
select dateadd(hour, 1, dte)
from d
where dateadd(hour, 1, dte) < '2018-01-01'
)
select d.*
from d
option (maxrecursion 0);
虽然递归速度惊人地快,但如果您需要多次使用它,您可能需要考虑使用数字表或将其存储在临时表或永久表中。
使用 rCTE 的另一种方法是计数表,因为它不是 RBAR:
DECLARE @TopDate date = '20550101';
WITH N AS(
SELECT *
FROM (VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) V(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS I
FROM N N1
CROSS JOIN N N2
CROSS JOIN N N3
CROSS JOIN N N4
CROSS JOIN N N5
CROSS JOIN N N6)
SELECT DATEADD(HOUR, I, '20170101') AS DateValue
FROM Tally
WHERE DATEADD(HOUR, I, '20170101') < @TopDate;
您可以使用单个查询来实现它。您所需要的只是计数(数字)表:
WITH tally(n) AS (
SELECT ROW_NUMBER() OVER(ORDER BY 1/0)-1
FROM master..spt_values s1, master..spt_values s2, master..spt_values s3
)
-- INSERT INTO calendar(col_name)
SELECT DATEADD(HOUR,n,'20170101') AS d
FROM tally
WHERE DATEADD(HOUR,n,'20170101') <= '20180101'
在 SQL Server 2022 中,您可以使用 GENERATE_SERIES 来执行此操作。请参阅https://www.sqlservercentral.com/articles/using-of-generate_series
--Generate a calendar table with 1 record per hour
SELECT DateDiff(hour,'2017-01-01','2018-01-01') as HoursTillEndOfYear;
SELECT DATEADD(HOUR, Hours.value -1 , '2017-01-01') dte
FROM GENERATE_SERIES(1,DateDiff(hour,'2017-01-01','2018-01-01')) Hours