Постановка проблемы: Я работаю в пожарной части и занимаюсь статистическим анализом моих данных. Одна проблема состоит в том, чтобы генерировать количество вызовов на обслуживание для каждого часа каждого дня в течение календарного года. Мне нужен стол, который можно присоединить к инцидентам с пожарами, которые происходят каждый день года и каждый час каждого дня. Я надеюсь на следующее (используя военное время)

1 января 2017 00:00:00

1 января 2017 00:00:00

1 января 2017 года 01:00:00

1 января 2017 02:00:00

1 января 2017 03:00:00

1 января 2017 04:00:00

1 января 2017 года 05:00:00

1 января 2017 г. 06:00:00

1 января 2017 года 07:00:00

1 января 2017 года 08:00:00

И т.д. до конца года

31 декабря 2017 года 21:00:00

31 декабря 2017 года 22:00:00

31 декабря 2017 года 23:00:00

Конец года

Эта таблица позволит мне присоединиться к таблице пожарных происшествий, и я смогу статистически рассчитать количество инцидентов для каждого часа дня и для каждого дня года. Расчетная таблица необходима, потому что в таблице пожарных происшествий есть пробелы. Например; 1 января в 01:00, 02:00 и 03:00 не поступали экстренные вызовы. Поэтому я не могу сделать расчет с использованием таблицы пожарных происшествий, потому что нет данных о том, когда не поступали звонки. Таблица пожарных происшествий с пробелами выглядит следующим образом:

Интервал времени, адрес инцидента

1 января 2017 00:00:00, улица Вязов 123

1 января 2017 04:00:00, улица Дуба 456

1 января 2017 05:00:00, 789 Maple Street

(Обратите внимание, что в часы 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 

Запрос вернет то, что я надеюсь достичь:

Интервал времени, Количество Инцидентов

1 января 2017 00:00:00, 5

1 января 2017 года 01:00:00, 0

1 января 2017 02:00:00, 0

1 января 2017 03:00:00, 0

1 января 2017 04:00:00, 2

1 января 2017 года 05:00:00, 1

(Обратите внимание, что часы 0100, 0200 и 0300 имеют нулевое количество вызовов. Это то, что я хочу! Теперь я могу создать гистограмму, показывающую, сколько часов было 0 вызовов. Или я могу рассчитать среднее значение, которое учитывает нулевые вызовы для части дня.)

Что я пробовал: Я попробовал следующее, но не могу понять, как создать из этого таблицу и как сделать ее готовым продуктом, как вы можете видеть ниже в пункте «Вопрос».

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, которая будет иметь каждый день года и каждый час каждого дня. Мой пример снова

1 января 2017 00:00:00

1 января 2017 года 01:00:00

1 января 2017 02:00:00

1 января 2017 03:00:00

1 января 2017 04:00:00

1 января 2017 года 05:00:00

1 января 2017 г. 06:00:00

1 января 2017 года 07:00:00

1 января 2017 года 08:00:00

И т.д. до конца года

31 декабря 2017 года 21:00:00

31 декабря 2017 года 22:00:00

31 декабря 2017 года 23:00:00

Конец года

2
David Fort Myers 21 Авг 2018 в 18:42

3 ответа

Лучший ответ

Простой метод использует рекурсию:

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);

Хотя рекурсия на удивление быстрая, если вам понадобится это несколько раз, вы можете рассмотреть вопрос о наличии таблицы чисел или ее сохранении во временной или постоянной таблице.

2
Gordon Linoff 21 Авг 2018 в 15:45

Вы можете достичь этого с помощью одного запроса. Все, что вам нужно, это таблица подсчета (номер):

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'

Демонстрация Rextester

1
Lukasz Szozda 21 Авг 2018 в 15:57

Альтернативным методом использования 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;
1
Larnu 21 Авг 2018 в 15:47
51952290