SQL Query to return 24 hour, hourly count even when no values exist?

前端 未结 6 1353
-上瘾入骨i
-上瘾入骨i 2021-01-14 08:53

I\'ve written a query that groups the number of rows per hour, based on a given date range.

SELECT CONVERT(VARCHAR(8),TransactionTime,101) + \' \' + CONVERT(         


        
相关标签:
6条回答
  • 2021-01-14 09:23

    You do this by building first the 23 hours table, the doing an outer join against the transactions table. I use, for same purposes, a table valued function:

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

    Then I can use the TVF in queries that need to get 'per-hour' basis data, even for missing intervals in the data:

    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
    

    Updated

    Example of a TVF that returns 24hours between any arbitrary dates:

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

    Note that since master.dbo.spt_values contains only 2048 numbers, the function will not work between dates further apart than 2048 hours.

    0 讨论(0)
  • 2021-01-14 09:23

    So going back to using Remus' original function, I've re-used it in a recursive call and storing the results in a temp table:

    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 讨论(0)
  • 2021-01-14 09:32

    I've run into a version of this problem before. The suggestion that worked the best was to setup a table (temporary, or not) with the hours of the day, then do an outer join to that table and group by datepart('h', timeOfRecord).

    I don't remember why, but probably due to lack of flexibility because of the need for the other table, I ended up using a method where I group by whatever datepart I want and order by the datetime, then loop through and fill any spaces that are skipped with a 0. This approach worked well for me because I'm not reliant on the database to do all my work for me, and it's also MUCH easier to write an automated test for it.

    0 讨论(0)
  • 2021-01-14 09:40

    Step 1, Create #table or a CTE to generate a hours days table. Outer loop for days and inner loop hours 0-23. This should be 3 columns Date, Days, Hours.

    Step 2, Write your main query to also have days and hours columns and alias it so you can join it. CTE's have to be above this main query and pivots should be inside CTE's for it to work naturally.

    Step 3, Do a select from step 1 table and Left join this Main Query table

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

    You can also create a order by if your date columns like

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

    Guidlines

    This will then give you all data for hours and days and including zeros for hours with no matches to do that use isnull([col1],0) as [col1].

    You can now graph facts against days and hours.

    0 讨论(0)
  • 2021-01-14 09:40

    You have just discovered the value of the NUMBERS table. You need to create a table with a single column containing the numbers 0 to 23 in it. Then you join again this table using an OUTER join to ensure you always get 24 rows returned.

    0 讨论(0)
  • 2021-01-14 09:42

    group by datepart('hour', thetime). to show those hours with no values you'd have to left join a table of times against the grouping (coalesce(transaction.amount, 0))

    0 讨论(0)
提交回复
热议问题