即使没有值,SQL Query也会返回24小时,每小时计数?

时间:2010-03-03 21:28:18

标签: sql-server tsql count group-by

我编写了一个查询,根据给定的日期范围对每小时的行数进行分组。

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?

感谢。

更新

使用下面的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 个答案:

答案 0 :(得分:4)

首先构建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

<强>更新

在任意日期之间返回24小时的TVF示例:

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小时之后的日期之间不起作用。

答案 1 :(得分:3)

您刚刚发现了NUMBERS表的值。您需要创建一个包含单个列的表,其中包含数字0到23。然后使用OUTER连接再次加入此表,以确保始终返回24行。

答案 2 :(得分:1)

所以回到使用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

答案 3 :(得分:0)

按日期分组('小时',时间)。显示那些没有值的小时你必须加入一个时间表来对抗分组(coalesce(transaction.amount,0))

答案 4 :(得分:0)

之前我遇到过这个问题的版本。最好的建议是设置一个表(临时的或不是临时的)与一天中的小时数,然后通过datepart('h',timeOfRecord)对该表和group进行外连接。

我不记得为什么,但可能由于缺乏灵活性,因为需要另一个表,我最终使用的方法是按照我想要的任何日期部分进行分组并按日期时间排序,然后循环执行填充任何用0跳过的空格。这种方法对我来说很有效,因为我不依赖于数据库为我完成所有工作,而且为它编写自动化测试也更容易。

答案 5 :(得分:0)

第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]

您现在可以根据日期和时间绘制事实。