TSQL:无法在COUNT(*)上执行聚合函数AVG以查找一天中最繁忙的时段

时间:2009-11-13 18:44:54

标签: sql sql-server sql-server-2005 tsql aggregate-functions

考虑一个包含日志数据的SQL Server表。重要的部分是:

CREATE TABLE [dbo].[CustomerLog](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [CustID] [int] NOT NULL,
    [VisitDate] [datetime] NOT NULL,
 CONSTRAINT [PK_CustomerLog] PRIMARY KEY CLUSTERED ([ID] ASC)) ON [PRIMARY]

此处的查询是查找当天的访问分布按小时。我们有兴趣查看给定日期范围内小时平均访问次数的分布情况。  Visits sample

查询结果如下:

HourOfDay   Avg.Visits.In.Hour
0            24
1            16
5            32
6            89
7           823
etc.etc.

目的是编写一个这样的查询

SELECT  DATEPART(hh, VisitDate)
        ,AVG(COUNT(*))    
FROM    CustomerLog
WHERE   VisitDate   BETWEEN 'Jan 1 2009' AND 'Aug 1 2009'
GROUP BY   DATEPART(hh, VisitDate)

但这不是有效的查询:

  

无法对包含聚合或子查询的表达式执行聚合函数。

问题:您如何重新编写此查询以收集平均总数(即代替AVG(COUNT(*))小时?

想象一下,此查询的结果会传递给PHB,{{3}}想知道当天最忙碌的小时

  • SQL Server 2005 +

2 个答案:

答案 0 :(得分:7)

使用内联视图:

SELECT DATEPART(hh, x.visitdate),
       AVG(x.num)
  FROM (SELECT t.visitdate,
               COUNT(*) 'num'
          FROM CUSTOMERLOG t
         WHERE t.visitdate BETWEEN 'Jan 1 2009' AND 'Aug 1 2009'
      GROUP BY t.visitdate) x
GROUP BY DATEPART(hh, x.visitdate)

使用CTE(SQL Server 2005+)等效:

WITH visits AS (
   SELECT t.visitdate,
          COUNT(*) 'num'
     FROM CUSTOMERLOG t
    WHERE t.visitdate BETWEEN 'Jan 1 2009' AND 'Aug 1 2009'
 GROUP BY t.visitdate)
   SELECT DATEPART(hh, x.visitdate),
         AVG(x.num)
    FROM visits x
GROUP BY DATEPART(hh, x.visitdate)

答案 1 :(得分:0)

已知天数,它等于DATEDIFF(day,CONVERT(DATETIME,'2009.01.01',120),CONVERT(DATETIME,'2009.09.01',120))。 您必须计算总和并将其除以所选范围内的天数:

SELECT  
  DATEPART(hh, VisitDate),
  CAST(COUNT(*) AS FLOAT) / DATEDIFF(day,CONVERT(DATETIME,'2009.01.01',120),CONVERT(DATETIME,'2009.09.01',120))
FROM CustomerLog
WHERE 
  (VisitDate >= CONVERT(DATETIME,'2009.01.01',120)) AND 
  (VisitDate < CONVERT(DATETIME,'2009.09.01',120))  
GROUP BY DATEPART(hh, VisitDate)

CAST(COUNT(*) AS FLOAT)可以获得更精确的结果,但您可以只留下COUNT(*)并获得整数结果。

如果使用参数,则为:

SELECT  
  DATEPART(hh, VisitDate),
  CAST(COUNT(*) AS FLOAT) / DATEDIFF(day,@beginningDate,@endDate)
FROM CustomerLog
WHERE 
  (VisitDate >= @beginningDate) AND 
  (VisitDate < @endDate)  
GROUP BY DATEPART(hh, VisitDate)

如果你想要1月的结果,你必须使用@beginningDate ='2009.01.01',@ endDate ='2009.02.01'。