【问题标题】:T-SQL Time AveragingT-SQL 时间平均
【发布时间】:2009-06-16 11:39:27
【问题描述】:

我在 SQL Server 中有一个表,用于存储硬件的统计信息,表中的行表示给定秒的数据。例如,它包含以下列:

timestamp (DateTime)
value (int)

我想要做的是从表中选择给定日期/时间范围的数据,但以给定时间段(例如 1 分钟、5 分钟、1 天等)的平均值的方式返回它给定范围之间。所以一个小时我会有 60 行 1 分钟的平均值。

我从哪里开始?有人有什么意见或想法吗?

【问题讨论】:

    标签: sql-server tsql datetime average


    【解决方案1】:

    您可以在时间戳的 DatePart 上进行选择和分组。

    例如:

    SELECT
        DATEPART(hh, [timestamp]),
        DATEPART(mi, [timestamp]),
        AVG([value])
    FROM
        YourTable
    WHERE
        [timestamp] BETWEEN '2009-01-01 00:00:00.000' AND '2009-02-01 00:00:00.000'
    GROUP BY
        DATEPART(hh, [timestamp]),
        DATEPART(mi, [timestamp])
    

    编辑:对于更复杂的时间跨度(例如 5 分钟),您可以按如下方式对日期部分进行除法。

    DATEPART(mi, [timestamp]) / 5 * 5
    

    【讨论】:

      【解决方案2】:
      WITH    cal(m) AS
              (
              SELECT  1
              UNION ALL
              SELECT  m + 1
              FROM    cal
              WHERE   m < 60
              )
      SELECT  DATEADD(minute, m, @start), AVG(value)
      FROM    cal
      LEFT JOIN
              timestamp
      ON      timestamp > DATEADD(minute, m, @start)
              AND timestamp <= DATEADD(minute, m + 1, @start)
      GROUP BY
              m
      

      这将选择给定小时内所有分钟的平均值,即使是没有记录的那些。

      【讨论】:

      • 用递归 CTE 构建数字表,以前没见过 - 我喜欢!
      【解决方案3】:

      除了 Robin Day 的帖子之外,您还可以按 5 分钟间隔分组,例如:

      GROUP BY
          DATEPART(hh, [timestamp]),
          DATEPART(mi, [timestamp]) / 5
      

      如果您想跨越几天,请按 dy 分组,表示一年中的某天:

      GROUP BY
          DATEPART(dy, [timestamp]),
          DATEPART(hh, [timestamp]),
          DATEPART(mi, [timestamp]) / 5
      

      【讨论】:

        【解决方案4】:

        如果您要对该数据具有较高的读/写比率,您可能需要考虑使用索引视图。我在各地都使用这种方法来按时间段进行汇总。我刚到blogging the example,这里是代码:

        create table timeSeries (
            timeSeriesId int identity primary key clustered
            ,updateDate datetime not null
            ,payload float not null
        )
        
        insert timeSeries values ('2009-06-16 12:00:00', rand())
        insert timeSeries values ('2009-06-16 12:00:59', rand())
        insert timeSeries values ('2009-06-16 12:01:00', rand())
        insert timeSeries values ('2009-06-16 12:59:00', rand())
        insert timeSeries values ('2009-06-16 01:00:00', rand())
        insert timeSeries values ('2009-06-16 1:30:00', rand())
        insert timeSeries values ('2009-06-16 23:59:00', rand())
        insert timeSeries values ('2009-06-17 00:01:00', rand())
        insert timeSeries values ('2009-06-17 00:01:30', rand())
        
        
        create view timeSeriesByMinute_IV with schemabinding as
        select
            dayBucket = datediff(day, 0, updateDate)
            ,minuteBucket = datediff(minute, 0, (updateDate - datediff(day, 0, updateDate)))
            ,payloadSum = sum(payLoad)
            ,numRows = count_big(*) 
        from dbo.timeSeries
        group by 
            datediff(day, 0, updateDate)
            ,datediff(minute, 0, (updateDate - datediff(day, 0, updateDate)))
        go
        
        create unique clustered index CU_timeSeriesByMinute_IV on timeSeriesByMinute_IV (dayBucket, minuteBucket)
        go
        
        
        create view timeSeriesByMinute as
        select
            dayBucket
            ,minuteBucket
            ,payloadSum
            ,numRows
            ,payloadAvg = payloadSum / numRows
        from dbo.timeSeriesByMinute_IV with (noexpand)
        go
        
        declare @timeLookup datetime, @dayBucket int, @minuteBucket int
        select 
            @timeLookup = '2009-06-16 12:00:00'
            ,@dayBucket = datediff(day, 0, @timeLookup)
            ,@minuteBucket = datediff(minute, 0, (@timeLookup - datediff(day, 0, @timeLookup)))
        
        select * from timeSeriesByMinute where dayBucket = @dayBucket and minuteBucket = @minuteBucket
        

        您可以在代码块的末尾看到示例查找。显然,您可以定义要查询的范围,而不仅仅是寻找特定的 dayBucket/minuteBucket 对。

        【讨论】:

          【解决方案5】:

          如果不进行以下更改,我无法让 Quassnoi 的答案正常工作:

          WITH    cal(m) AS
              (
              SELECT  1
              UNION ALL
              SELECT  m + 1
              FROM    cal
              WHERE   m < 60
              )
          SELECT  DATEADD(minute, m, @start) m, AVG(value)
          FROM    cal
          LEFT JOIN
              YourTable
          ON      timestamp > DATEADD(minute, m, @start)
              AND timestamp <= DATEADD(minute, m + 1, @start)
          GROUP BY
              m
          

          【讨论】:

            猜你喜欢
            • 2010-11-03
            • 2019-03-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多