将 DateTime 分组为 5、15、30 和 60 分钟间隔

Posted

技术标签:

【中文标题】将 DateTime 分组为 5、15、30 和 60 分钟间隔【英文标题】:Group DateTime into 5,15,30 and 60 minute intervals 【发布时间】:2012-04-06 13:55:51 【问题描述】:

我正在尝试将一些记录分组为 5 分钟、15 分钟、30 分钟和 60 分钟的间隔:

SELECT AVG(value) as "AvgValue",
sample_date/(5*60) as "TimeFive"
FROM DATA
WHERE id = 123 AND sample_date >= 3/21/2012

我想运行几个查询,每个查询都会将我的平均值分组到所需的时间增量中。所以 5 分钟的查询会返回如下结果:

AvgValue  TimeFive
6.90      1995-01-01 00:05:00
7.15      1995-01-01 00:10:00
8.25      1995-01-01 00:15:00

30 分钟的查询将导致:

AvgValue  TimeThirty 
6.95      1995-01-01 00:30:00
7.40      1995-01-01 01:00:00

datetime 列采用yyyy-mm-dd hh:mm:ss 格式

我的datetime 列出现隐式转换错误。非常感谢任何帮助!

【问题讨论】:

“分组到不同的间隔”对我来说并不明显。回想一下结果必然是列中的行,您能否展示数据的示例行的样子?特别是我不清楚,一个 id 会在 1 个 5 分钟组中,也在一个 15 分钟组中,也在一个 30 分钟组中......等等。例如,如果每条记录将分为 4 组,那么您希望结果中的列的名称是什么? 另外,如果您要发布一些 sql 问题,1) 始终使用特定类型的服务器标记问题(我从标题中看到它是 MS SQL,但如果您使用他们建议你可以指定一个版本的标签)和 2)你可以通过花时间设置一个 sql fiddle(sqlfiddle.org)来降低“弄清楚问题意味着什么的成本”;它可以让您输入要查询的示例数据。 只是为了澄清一些事情:SQL Server 中的DATETIME从不以基于字符串的格式存储 - 它在内部存储为两个 4 字节的 INT 值。该格式可能是您的默认演示文稿 - 但它以该格式存储! sample_date 实际上是 datetime 类型吗?如果是这样,它没有格式(这很好) sample_date 是类型(smalldatetime,not null) 【参考方案1】:

使用

datediff(minute, '1990-01-01T00:00:00', yourDatetime)

将为您提供自 1990-1-1 以来的分钟数(您可以使用所需的基准日期)。

然后您可以除以 5、15、30 或 60,并按此除法的结果进行分组。 我已经检查过它将被评估为整数除法,因此您将获得一个可用于分组的整数。

group by datediff(minute, '1990-01-01T00:00:00', yourDatetime) /5

更新由于原始问题已被编辑为要求在分组后以日期时间格式显示数据,因此我添加了这个简单的查询,它将满足 OP 的要求:

-- This convert the period to date-time format
SELECT 
    -- note the 5, the "minute", and the starting point to convert the 
    -- period back to original time
    DATEADD(minute, AP.FiveMinutesPeriod * 5, '2010-01-01T00:00:00') AS Period,
    AP.AvgValue
FROM
    -- this groups by the period and gets the average
    (SELECT
        P.FiveMinutesPeriod,
        AVG(P.Value) AS AvgValue
    FROM
        -- This calculates the period (five minutes in this instance)
        (SELECT 
            -- note the division by 5 and the "minute" to build the 5 minute periods
            -- the '2010-01-01T00:00:00' is the starting point for the periods
            datediff(minute, '2010-01-01T00:00:00', T.Time)/5 AS FiveMinutesPeriod,
            T.Value
        FROM Test T) AS P
    GROUP BY P.FiveMinutesPeriod) AP

注意:为了清楚起见,我将其分为 3 个子查询。你应该从里到外阅读它。当然,它可以写成一个单一的、紧凑的查询

注意:如果您更改期间和开始日期时间,您可以获得所需的任何间隔,例如从给定日期开始的周数,或者您可能需要的任何时间

如果您想为此查询生成测试数据,请使用:

CREATE TABLE Test
( Id INT IDENTITY PRIMARY KEY,
Time DATETIME,
Value FLOAT)

INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:00:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:03:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:04:45', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:07:21', 20)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:10:25', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:11:22', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:14:47', 30)

执行查询的结果是这样的:

Period                     AvgValue
2012-03-22 00:00:00.000    10
2012-03-22 00:05:00.000    20
2012-03-22 00:10:00.000    30

【讨论】:

我建议19900101 而不是1990-1-1,因为用于指定没有时间的日期的虚线格式是不明确的。 (当然,在这个具体的例子中是可以的,但是如果月份和日期不同,SQL Server 可能会以两种方式之一进行解释,或者引发转换错误) 感谢您的建议。我运行了这个查询,它似乎将我的平均值分组到结果中的行中,但我无法让聚合时间值出现在查询结果中。我已经编辑了你的帖子来描述我试图让时间列出现在查询结果中 我尝试使用 Damien_The_Unbeliever 描述的 '19900101' 格式,这似乎确实有效,但是我没有看到初始查询结果有任何差异。感谢您的建议。 @jrubengb:使用DATEADD 和简单的数学从数字中获取日期时间。我已经更新了我的答案。 关于之前关于日期格式的 cmets,YYYY-MM-DDThh:mm:ss 是在每个 SQL Server(以及任何其他知道这种格式的软件)中普遍理解的 ISO 格式,并且独立于语言或任何其他相关设置。【参考方案2】:

基于@JotaBe 的回答(我无法对此发表评论——否则我会),您也可以尝试类似这样的不需要子查询的方法。

 SELECT 
    AVG(value) AS 'AvgValue',

    -- Add the rounded seconds back onto epoch to get rounded time
    DATEADD(
        MINUTE,
        (DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30) * 30,
        '1990-01-01T00:00:00'
    )      AS 'TimeThirty'

 FROM YourTable
 -- WHERE your_date > some max lookback period
 GROUP BY
    (DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30)

此更改删除了临时表和子查询。它使用相同的核心逻辑按 30 分钟间隔进行分组,但是,当将数据作为结果的一部分返回时,我只是反转间隔计算以获取舍入的日期和时间。

【讨论】:

【参考方案3】:

所以,如果你用 google 搜索了这个,但你需要在 mysql 中进行,这是我的情况:

在 MySQL 中你可以这样做

GROUP BY
CONCAT(
    DATE_FORMAT(`timestamp`,'%m-%d-%Y %H:'),
    FLOOR(DATE_FORMAT(`timestamp`,'%i')/5)*5
)

【讨论】:

...但问题是关于 sql server 而不是我的 sql?! 这个问题是关于sql-server,但它也被标记为纯sql,所以这将包括所有口味【参考方案4】:

这将完全满足您的需求

替换 dt - 您的日期时间 c - 调用字段 astro_transit1 - 您的表 300 参考 5 分钟,因此每次添加 300 以增加时间间隔

SELECT FROM_UNIXTIME( 300 * ROUND( UNIX_TIMESTAMP( r.dt ) /300 ) ) AS 5datetime, ( SELECT r.c FROM astro_transit1 ra WHERE ra.dt = r.dt ORDER BY ra.dt DESC LIMIT 1 ) AS first_val FROM astro_transit1 r GROUP BY UNIX_TIMESTAMP( r.dt ) DIV 300 LIMIT 0 , 30

【讨论】:

以上是关于将 DateTime 分组为 5、15、30 和 60 分钟间隔的主要内容,如果未能解决你的问题,请参考以下文章

按移位的 DATETIME 字段和按 ID 的 PIVOT 分组

按日期分组时将整数行转换为列

datetime处理日期和时间

datetime处理日期和时间

datetime处理日期和时间

datetime处理日期和时间