SQL-запрос для возврата 24 часа, почасовой подсчет, даже если нет значений? - PullRequest
2 голосов
/ 04 марта 2010

Я написал запрос, который группирует количество строк в час на основе заданного диапазона дат.

SELECT CONVERT(VARCHAR(8),TransactionTime,101) + ' ' + CONVERT(VARCHAR(2),TransactionTime,108) as TDate, 
    COUNT(TransactionID) AS TotalHourlyTransactions
    FROM MyTransactions WITH (NOLOCK)
    WHERE TransactionTime BETWEEN CAST(@StartDate AS SMALLDATETIME) AND CAST(@EndDate AS SMALLDATETIME)
    AND TerminalId = @TerminalID
    GROUP BY CONVERT(VARCHAR(8),TransactionTime,101) + ' ' + CONVERT(VARCHAR(2),TransactionTime,108)
    ORDER BY TDate ASC

Который отображает что-то вроде этого:

02/11/20 07 4
02/11/20 10 1
02/11/20 12 4
02/11/20 13 1
02/11/20 14 2
02/11/20 16 3

Предоставление количества транзакций и заданного часа дня.

Как я могу отображать все часы дня - от 0 до 23 и показывать 0 для тех, у которых нет значений?

Спасибо.

UPDATE

Использование приведенного ниже tvf работает у меня один день, однако я не уверен, как заставить его работать для диапазона дат.

Использование временной таблицы за 24 часа:

 -- temp table to store hours of the day    
 DECLARE @tmp_Hours TABLE ( WhichHour SMALLINT )

 DECLARE @counter SMALLINT
 SET @counter = -1
 WHILE @counter < 23 
    BEGIN
        SET @counter = @counter + 1
      --print 
        INSERT  INTO @tmp_Hours
                ( WhichHour )
        VALUES  ( @counter )
    END 

    SELECT MIN(CONVERT(VARCHAR(10),[dbo].[TerminalTransactions].[TransactionTime],101)) AS TDate, [@tmp_Hours].[WhichHour], CONVERT(VARCHAR(2),[dbo].[TerminalTransactions].[TransactionTime],108) AS TheHour,
        COUNT([dbo].[TerminalTransactions].[TransactionId]) AS TotalTransactions, 
        ISNULL(SUM([dbo].[TerminalTransactions].[TransactionAmount]), 0) AS TransactionSum
    FROM [dbo].[TerminalTransactions] RIGHT JOIN @tmp_Hours ON [@tmp_Hours].[WhichHour] = CONVERT(VARCHAR(2),[dbo].[TerminalTransactions].[TransactionTime],108) 
    GROUP BY [@tmp_Hours].[WhichHour], CONVERT(VARCHAR(2),[dbo].[TerminalTransactions].[TransactionTime],108),  COALESCE([dbo].[TerminalTransactions].[TransactionAmount], 0)

Дает мне результат:

TDate      WhichHour TheHour TotalTransactions TransactionSum
---------- --------- ------- ----------------- ---------------------
02/16/2010 0         00      4                 40.00
NULL       1         NULL    0                 0.00
02/14/2010 2         02      1                 10.00
NULL       3         NULL    0                 0.00
02/14/2010 4         04      28                280.00
02/14/2010 5         05      11                110.00
NULL       6         NULL    0                 0.00
02/11/2010 7         07      4                 40.00
NULL       8         NULL    0                 0.00
02/24/2010 9         09      2                 20.00

Так как я могу получить это для группы правильно?

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

Спасибо.

Ответы [ 6 ]

4 голосов
/ 04 марта 2010

Вы делаете это, сначала создав 23-часовую таблицу, выполнив внешнее соединение с таблицей транзакций. Для этих же целей я использую табличную функцию:

create function tvfGetDay24Hours(@date datetime)
returns table
as return (
select dateadd(hour, number, cast(floor(cast(@date as float)) as datetime)) as StartHour
  , dateadd(hour, number+1, cast(floor(cast(@date as float)) as datetime)) as EndHour
from master.dbo.spt_values
where number < 24 and type = 'p');

Затем я могу использовать TVF в запросах, которые должны получать данные за час, даже при пропущенных интервалах в данных:

select h.StartHour, t.TotalHourlyTransactions
from tvfGetDay24Hours(@StartDate) as h
outer apply (
  SELECT 
    COUNT(TransactionID) AS TotalHourlyTransactions
    FROM MyTransactions 
    WHERE TransactionTime BETWEEN h.StartHour and h.EndHour
    AND TerminalId = @TerminalID) as t
order by h.StartHour

Обновлено

Пример TVF, который возвращает 24 часа между произвольными датами:

create function tvfGetAnyDayHours(@dateFrom datetime, @dateTo datetime)
returns table
as return (
select dateadd(hour, number, cast(floor(cast(@dateFrom as float)) as datetime)) as StartHour
  , dateadd(hour, number+1, cast(floor(cast(@dateFrom as float)) as datetime)) as EndHour
from master.dbo.spt_values
where type = 'p'
and number < datediff(hour,@dateFrom, @dateTo) + 24);

Обратите внимание, что поскольку master.dbo.spt_values ​​содержит только 2048 чисел, функция не будет работать между датами, которые находятся дальше 2048 часов.

3 голосов
/ 04 марта 2010

Вы только что обнаружили значение таблицы NUMBERS. Вам необходимо создать таблицу с одним столбцом, содержащим числа от 0 до 23. Затем вы снова присоединяетесь к этой таблице, используя соединение OUTER, чтобы всегда возвращать 24 строки.

1 голос
/ 05 марта 2010

Итак, возвращаясь к использованию оригинальной функции Remus, я повторно использовал ее в рекурсивном вызове и сохранял результаты во временной таблице:

DECLARE @count INT
DECLARE @NumDays INT
DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
DECLARE @CurrentDay DATE

    DECLARE @tmp_Transactions TABLE 
    (
        StartHour DATETIME,
        TotalHourlyTransactions INT
    )   

SET @StartDate = '2000/02/10'
SET @EndDate = '2010/02/13'
SET @count = 0
SET @NumDays = DateDiff(Day, @StartDate, @EndDate)
WHILE @count < @NumDays 
    BEGIN
        SET @CurrentDay = DateAdd(Day, @count, @StartDate)
        INSERT INTO @tmp_Transactions (StartHour, TotalHourlyTransactions)
            SELECT  h.StartHour ,
                    t.TotalHourlyTransactions
            FROM    tvfGetDay24Hours(@CurrentDay) AS h
                    OUTER APPLY ( SELECT    COUNT(TransactionID) AS TotalHourlyTransactions
                                  FROM      [dbo].[TerminalTransactions]
                                  WHERE     TransactionTime BETWEEN h.StartHour AND h.EndHour
                                            AND TerminalId = 4
                                ) AS t
            ORDER BY h.StartHour
        SET @count = @Count + 1
    END 

SELECT *
FROM @tmp_Transactions
0 голосов
/ 04 ноября 2015

Шаг 1 , Создать #table или CTE для создания таблицы часов и дней. Внешний цикл для дней и внутренний цикл часов 0-23. Это должно быть 3 столбца Дата, Дни, Часы.

Шаг 2 . Укажите основной запрос, в котором также есть столбцы дней и часов и псевдоним, чтобы вы могли присоединиться к нему. CTE должны быть выше этого основного запроса, а шарниры должны быть внутри CTE для его естественной работы.

Шаг 3 , сделать выбор из таблицы шага 1 и слева присоединиться к этой таблице основного запроса

ON A.[DATE] = B.[DATE] 
AND A.[HOUR] = B.[HOUR]

Вы также можете создать заказ, если ваши столбцы даты, такие как

ORDER BY substring(CONVERT(VARCHAR(15), A.[DATE], 105),4,2)

Guidlines

Затем вы получите все данные за часы и дни, включая нули для часов без совпадений, для которых используется isnull([col1],0) as [col1].

Теперь вы можете отображать факты по дням и часам.

0 голосов
/ 04 марта 2010

Я столкнулся с версией этой проблемы раньше. Лучше всего было настроить таблицу (временную или нет) с часами дня, а затем выполнить внешнее соединение с этой таблицей и сгруппировать по дате (h, timeOfRecord).

Я не помню почему, но, вероятно, из-за недостатка гибкости из-за необходимости в другой таблице я в итоге использовал метод, в котором я группирую по любой части даты, которую хочу, и упорядочиваю по дате, а затем перебираю заполнить все пробелы, которые пропущены с 0. Этот подход хорошо сработал для меня, потому что я не полагаюсь на базу данных, чтобы выполнить всю мою работу за меня, и также НАМНОГО проще написать для нее автоматический тест.

0 голосов
/ 04 марта 2010

сгруппировать по дате («час», время). чтобы показать эти часы без значений, вам нужно было бы присоединиться к таблице времен по группам (coalesce (action.amount, 0))

...