【问题标题】:How to group time by hour or by 10 minutes如何按小时或按 10 分钟分组时间
【发布时间】:2011-06-27 12:32:59
【问题描述】:

喜欢我做的时候

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date]

如何指定分组时间?

MS SQL 2008

第二次编辑

我在努力

SELECT MIN([Date]) AS RecT, AVG(Value)
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY (DATEPART(MINUTE, [Date]) / 10)
  ORDER BY RecT

将 %10 更改为 /10。是否可以在没有毫秒的情况下输出日期?

【问题讨论】:

    标签: sql sql-server-2008 tsql group-by


    【解决方案1】:

    终于搞定了

    GROUP BY
    DATEPART(YEAR, DT.[Date]),
    DATEPART(MONTH, DT.[Date]),
    DATEPART(DAY, DT.[Date]),
    DATEPART(HOUR, DT.[Date]),
    (DATEPART(MINUTE, DT.[Date]) / 10)
    

    【讨论】:

    • 我做了ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5,所以当我查看数据时,它与最近的时隙相关
    • 年月日可以简化为DATE(DT.[Date])
    • @Keelan 对我不起作用 - 但是 CONVERT(date, DT.[Date]) 可以。
    • datepart(hour, workingPolicy.workingHours)/2.01.5datepart(hour, '1900-01-01 09:00:30.000')/2.04.5 ,我不明白为什么? 注意:workingPolicy.workingHours=1900-01-01 09:00:30.000。请帮忙
    【解决方案2】:

    又短又甜

    我迟到了,但这并没有出现在任何现有的答案中:

    GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
    

    实际使用

    SELECT   DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
                                                                 AS [date_truncated],
             COUNT(*) AS [records_in_interval],
             AVG(aa.[value]) AS [average_value]
    FROM     [friib].[dbo].[archive_analog] AS aa
    -- WHERE aa.[date] > '1900-01-01'
    GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
    ORDER BY [date_truncated]
    

    细节和评论

    • MINUTE10 术语可以分别更改为任何DATEPART 和整数1,以分组到不同的时间间隔。

      • 例如10MINUTE 是十分钟间隔; 6HOUR 是 间隔六小时。

      • 如果您大量更改间隔,您可能会从DECLARE将其作为变量受益。

        DECLARE @interval int = 10;
        …
        GROUP BY DATEADD(MINUTE, … / @interval * @interval, '2000')
        
    • 这是一个DATETIME 值,这意味着:

      • 长时间间隔很好。其他一些答案在年份之间存在冲突。
      • 将它包含在 SELECT 语句中将为您的输出提供一列,该列的输出在您指定的级别被截断。
    • 截断整数除法(FLOOR 快捷方式)使SELECT 中显示的日期输出成为每个时间间隔的开始。如果您想要区间的中间或结尾,您可以在DATEADD 的第二个术语中调整除法,下面的粗体部分:

      • 间隔结束:…) / 10 * 10 + 10 , '2000'),归功于Daniel Elkington
      • 区间中间:…) / 10 * 10 + (10 / 2.0) , '2000')

    琐事

    '2000' 是一个“锚日期”,SQL 将围绕它执行日期数学运算。大多数示例代码使用0 作为锚点,但JereonH 发现在按秒或毫秒对最近的日期进行分组时会遇到整数溢出。2

    如果您的数据跨越几个世纪,3GROUP BY 中使用单个锚日期几秒或几毫秒仍会遇到溢出。对于这些查询,您可以要求每一行将分箱比较锚定到其自己日期的午夜:

    • 使用DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0) 而不是上面出现的'2000'。您的查询将完全不可读,但它会起作用。

    • 替代方案可能是 CONVERT(DATETIME, CONVERT(DATE, aa.[date])) 作为替代品。

    1 从技术上讲,您可以使用DATEPART 的最大值可以分为的任何整数。4 如果要将结果分组为 13 - 分钟的垃圾箱或 37 小时的垃圾箱,您最终可能会得到不均等的垃圾箱或时移的结果。
    2 数学表示 232 ≈ 4.29E+9。这意味着对于SECOND 中的DATEPART,两边各有 43 亿秒,相当于“锚定日期 ± 136 年”。同样,232 毫秒 ≈ 49.7 天。
    3 如果您的数据实际上跨越了几个世纪或几千年并且仍然准确 到秒或毫秒……恭喜!无论您在做什么,请继续这样做。
    4 如果您想知道为什么我们的时钟顶部有 12,请思考 5 is the only integer 如何从 6 (12 的一半)或以下不是 12 的因数。然后请注意,5 × 12 = 60。您有很多的箱尺寸选择,包括小时、分钟和秒。

    【讨论】:

    • 要四舍五入到最接近的 10 分钟,您可以使用 GROUP BY DATEADD(MINUTE, (DATEDIFF(MINUTE, 0, date_column) / 10 * 10) + 10, 0)(在 SELECT 子句中也是如此)。
    • 对于 SECOND 作为 DATEPART 我收到一条错误消息 (The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.)。似乎 MINUTE 是您可以使用这种方法的最小日期部分。
    • @jeroenh,你是对的。我已经添加了一个部分来讨论这个问题。 tldr:将0 更改为'2000'(引号很重要!)并再次尝试SECOND
    • @Michael 这项技术效果很好,但我确实注意到,如果给定切片的计数为零,则结果集中没有相应的行。
    • @howcheng 确实如此。为此,您可以对包含所有可能间隔的表进行右连接。您可以使用cursor 生成一个,但我觉得这超出了问题的范围。
    【解决方案3】:

    在 T-SQL 中,您可以:

    SELECT [Date]
      FROM [FRIIB].[dbo].[ArchiveAnalog]
      GROUP BY [Date], DATEPART(hh, [Date])
    

    按分钟使用DATEPART(mi, [Date])

    在 10 分钟内使用 DATEPART(mi, [Date]) / 10(就像 Timothy 建议的那样)

    【讨论】:

    • GROUP BY [Date], DATEPART(hh, [Date]) 是一团糟,不是吗?
    • @nCdy 应该没问题,否则会报错“...在选择列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中”
    • 如何同时按整个日期和按小时分组?我只是在使用 Min Date 。
    • 如果你使用 Min(Date) 那么你当然可以在 Group By 中取出 Date。
    • 分钟除以 10 将创建以 10 分钟为间隔的分组,这是我所需要的。模 10 没有意义。
    【解决方案4】:

    每隔 10 分钟,你会

    GROUP BY (DATEPART(MINUTE, [Date]) / 10)
    

    正如 tzup 和 Pieter888 已经提到的那样……间隔一个小时,只需

    GROUP BY DATEPART(HOUR, [Date])
    

    【讨论】:

    • 但在这里我将 1980 年的分钟与 2011 年的分钟组合在一起:S 我需要关心它。
    • % 会是正确的数学吗?那不会创建 10 个组。例如: 1 % 10 = 9 2 % 10 = 8 它甚至不一定是正确的时间分组。我认为只是一个正常的划分是正确的。除非 % 不是 sql 中的余数除法。
    • 分钟除以 10 将创建以 10 分钟为间隔的分组,这是我所需要的。模 10 没有意义。
    • 同意焦油。分组没有意义。
    【解决方案5】:

    应该是这样的

    select timeslot, count(*)  
    from 
        (
        select datepart('hh', date) timeslot
        FROM [FRIIB].[dbo].[ArchiveAnalog]  
        ) 
    group by timeslot
    

    (不是 100% 确定语法 - 我更像是 Oracle 人)

    在甲骨文中:

    SELECT timeslot, COUNT(*) 
    FROM
    (  
        SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot 
        FROM
        (
            SELECT l_time FROM mytab  
        )  
    ) GROUP BY timeslot 
    

    【讨论】:

      【解决方案6】:

      作者给出的原始答案效果很好。只是为了扩展这个想法,你可以做类似

      group by datediff(minute, 0, [Date])/10
      

      这将允许您按更长的时间分组,然后是 60 分钟,比如 720,即半天等。

      【讨论】:

      • 以上的 MySQL 查询是什么?
      • 这个肯定比公认的答案好。还可以设置任意日期而不是 0,并使生成的 datediff 更小: DATEDIFF(minute, CAST('2021-08-01' AS datetime), [Date])/10
      【解决方案7】:

      对于 MySql:

      GROUP BY
      DATE(`your_date_field`),
      HOUR(`your_date_field`),
      FLOOR( MINUTE(`your_date_field`) / 10);
      

      【讨论】:

        【解决方案8】:
        declare @interval tinyint
        set @interval = 30
        select dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0), sum(Value_Transaction)
        from Transactions
        group by dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0)
        

        【讨论】:

        • 欢迎来到stackoverflow。您需要充分解释您的答案,以及这对网站上已有的其他 10 个答案的补充。
        【解决方案9】:

        我的解决方案是使用一个函数创建一个包含日期间隔的表,然后将该表连接到我要使用表中的日期间隔分组的数据。 然后可以在呈现数据时轻松选择日期间隔。

        CREATE FUNCTION [dbo].[fn_MinuteIntervals]
            (
              @startDate SMALLDATETIME ,
              @endDate SMALLDATETIME ,
              @interval INT = 1
            )
        RETURNS @returnDates TABLE
            (
              [date] SMALLDATETIME PRIMARY KEY NOT NULL
            )
        AS
            BEGIN
                DECLARE @counter SMALLDATETIME
                SET @counter = @startDate
                WHILE @counter <= @endDate
                    BEGIN
                        INSERT INTO @returnDates VALUES ( @counter )
                        SET @counter = DATEADD(n, @interval, @counter)
                    END
                RETURN
            END
        

        【讨论】:

        • 在大型数据集上使用游标会非常非常慢。
        【解决方案10】:

        对于 SQL Server 2012,虽然我相信它可以在 SQL Server 2008R2 中工作,但我使用以下方法将时间切片到毫秒:

        DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % @msPerSlice, time)
        

        这是由:

        • 获取固定点和目标时间之间的毫秒数:
          @ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
        • 将这些毫秒划分为时间片的余数:
          @rms = @ms % @msPerSlice
        • 将该余数的负数与目标时间相加得到切片时间:
          DATEADD(MILLISECOND, -@rms, time)

        不幸的是,这会溢出微秒和更小的单位,因此更大、更精细的数据集需要使用不太方便的固定点。

        我没有对此进行严格的基准测试,并且我不是在大数据中,所以你的里程可能会有所不同,但性能并不比在我们的设备和数据集上尝试的其他方法明显差,并且开发人员方便任意切片的支出这对我们来说是值得的。

        【讨论】:

          【解决方案11】:
          select dateadd(minute, datediff(minute, 0, Date), 0),
                 sum(SnapShotValue)
          FROM [FRIIB].[dbo].[ArchiveAnalog]
          group by dateadd(minute, datediff(minute, 0, Date), 0)
          

          【讨论】:

          • 请在回答问题时提供一些解释。
          【解决方案12】:

          如果您想实际显示日期,并希望有一个变量分组,并希望能够将更大的时间范围指定为 60 分钟:

          DECLARE @minutes int
          SET @minutes = 90
          
          SELECT
              DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [Date]) / @minutes * @minutes, 0) as [Date],
              AVG([Value]) as [Value]
          FROM [FRIIB].[dbo].[ArchiveAnalog]
          GROUP BY
              DATEDIFF(MINUTE, 0, [Date]) / @minutes
          

          【讨论】:

            【解决方案13】:
            select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
            from [FRIIB].[dbo].[ArchiveAnalog]
            group by RecT
            order by RecT;
            

            将两个 600 替换为您想要分组的任意秒数。

            如果您经常需要这个并且表格没有改变,正如存档名称所暗示的那样,将日期(和时间)转换并存储为表格中的 unixtime 可能会更快一些。

            【讨论】:

              【解决方案14】:

              我知道我在这个节目上迟到了,但我使用了这个 - 非常简单的方法。这使您可以在没有任何舍入问题的情况下获得 60 分钟的切片。

              Select 
                 CONCAT( 
                          Format(endtime,'yyyy-MM-dd_HH:'),  
                          LEFT(Format(endtime,'mm'),1),
                          '0' 
                        ) as [Time-Slice]
              

              【讨论】:

                【解决方案15】:

                在 SQLite 中,为了按小时分组,你可以这样做:

                GROUP BY strftime('%H', [FRIIB].[dbo].[ArchiveAnalog].[Date]);
                

                每 10 分钟分组一次:

                GROUP BY strftime('%M', [FRIIB].[dbo].[ArchiveAnalog].[Date]) / 10;
                

                【讨论】:

                  【解决方案16】:

                  试试这个查询。它构成一列。 (参考@nobilist 回答)

                  GROUP BY CAST(DATE(`your_date_field`) as varchar) || ' ' || CAST(HOUR(`your_date_field`) as varchar) || ':' || CAST(FLOOR(minute(`your_date_field`) / 10) AS varchar) || '0' AS date_format
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2015-11-15
                    • 2016-04-15
                    • 2021-09-28
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-05-18
                    • 1970-01-01
                    • 2019-10-20
                    相关资源
                    最近更新 更多