Sql server SQL查询返回24小时、每小时计数,即使不存在值?

Sql server SQL查询返回24小时、每小时计数,即使不存在值?,sql-server,tsql,count,group-by,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 TransactionTim

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

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对我来说只适用一天,但是我不知道如何使它适用于一个日期范围

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
使用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)
那么,我如何才能让这个团队正常运作呢

另一个问题是,在某些日子里不会有交易,这些日子也需要出现


谢谢。

按日期分组部分“小时”,时间。要显示没有值的小时数,您必须根据分组coalescetransaction.amount 0加入一个时间表,我以前遇到过这个问题的一个版本。最有效的建议是设置一个临时表,或者不使用一天中的小时数,然后对该表进行外部联接,并按datepart'h',timeOfRecord分组


我不记得为什么,但可能是因为缺少灵活性,因为需要另一个表,我最终使用了一种方法,在这种方法中,我按我想要的任何datepart分组,按datetime排序,然后循环并用0填充跳过的任何空间。这种方法对我很有效,因为我不依赖数据库来完成我的所有工作,而且为它编写自动测试也容易得多。

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

首先构建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');
更新

返回任意日期之间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_值仅包含2048个数字,因此该函数在相隔2048小时的日期之间不起作用。

因此回到使用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
步骤1,创建表或CTE以生成小时-天表。外循环持续数天,内循环持续0-23小时。这应该是3列日期、天、小时

第2步,编写主查询,使其也包含days和hours列,并将其命名为别名,以便加入它。CTE必须位于该主查询之上,且枢轴应位于CTE内,以便其正常工作

步骤3,从步骤1表中选择并左键联接此主查询表

ON A.[DATE] = B.[DATE] 
AND A.[HOUR] = B.[HOUR]
您还可以创建订单,如果您的日期列

子字符串CONVERTVARCHAR15,A[日期],105,4,2

指南

然后,这将为您提供小时和天的所有数据,包括小时的零,没有匹配项,使用isnull[col1],0作为[col1]


现在,您可以将事实与天数和小时进行对比。

您希望报告多少天/哪些天的小时数?对于@start/@enddate范围,我要键入的内容:+1,我想是CTE,但此表函数将更好地集成到查询中!谢谢大家的建议。就在我昨晚离开之前,我想到了一个值为0-23的临时表,正如上面马克所建议的。再进一步,您将如何在几天和几年内执行此操作?Remus,最后一个查询将返回一天的值。但是,您如何在日期范围内使用tvf?您可以使用一个tvf,它接受两个日期,并在这两个日期之间每小时返回一次,因此您可以在任意范围内使用它。请记住,非常大的日期范围年份可能会影响性能,因为外部应用操作符将在该范围内每小时运行一次内部查询。我想做的是能够在一个日期范围内重用这个函数。既然集合可以通过范围内的每一天,那么脚本是什么呢?
ON A.[DATE] = B.[DATE] 
AND A.[HOUR] = B.[HOUR]