按分钟和两分钟间隔分组

dub*_*ech 8 sql-server-2008 sql-server

我有一个日期时间列,我可以轻松地在我的日期时间列上使用 group by 运行查询。但是,我想运行组查询

  • 1 分钟间隔
  • 2 分钟间隔
  • 5 分钟间隔
  • 1 小时间隔
  • 等等。

我该怎么做呢?

Aar*_*and 8

首先,有一个表格和一些示例数据可供使用:

USE tempdb;
GO
CREATE TABLE dbo.SomeTable(dt DATETIME);
GO
SET NOCOUNT ON;
GO
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -22, GETDATE());
GO 45
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -19, GETDATE());
GO 32
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -17, GETDATE());
GO 21
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -12, GETDATE());
GO 16
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -5, GETDATE());
GO 55
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -2, GETDATE());
GO 26
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -1, GETDATE());
GO 71
INSERT dbo.SomeTable(dt) SELECT GETDATE();
GO 14
Run Code Online (Sandbox Code Playgroud)

(我会在 sqlfiddle 中执行此操作,但我不确定它是否支持GO <int>多行,并且在INSERT超过 8000 个字符时会卡住。)

现在是一个存储过程:

CREATE PROCEDURE dbo.GetGroupedIntervals
    @MinuteInterval TINYINT = 1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE 
        @IntervalCount INT, @StartDate SMALLDATETIME;

    SELECT
        @StartDate = DATEADD(MINUTE, -1, MIN(dt)), 
        @IntervalCount = (DATEDIFF(MINUTE, MIN(dt), MAX(dt)) 
            + @MinuteInterval) / @MinuteInterval
          FROM dbo.SomeTable -- WHERE ...;

    ;WITH dates(s,e) AS
    (
        SELECT 
            DATEADD(MINUTE, @MinuteInterval*(n.n-1), @StartDate),
            DATEADD(MINUTE, @MinuteInterval*(n.n), @StartDate)
        FROM
        (
          SELECT 
            TOP (@IntervalCount) ROW_NUMBER() OVER (ORDER BY o.[object_id])
            FROM sys.all_objects AS o CROSS JOIN sys.all_columns AS c
            ORDER BY o.[object_id]
        ) AS n(n)
    )
    SELECT StartDate = d.s, c = COUNT(s.dt) 
    FROM dates AS d
    LEFT OUTER JOIN dbo.SomeTable AS s
        ON s.dt >= d.s AND s.dt < d.e
        -- AND any filter criteria for dbo.SomeTable?
    GROUP BY d.s
    ORDER BY d.s;
END
GO
Run Code Online (Sandbox Code Playgroud)

以及一些示例用法:

EXEC dbo.GetGroupedIntervals @MinuteInterval = 1;
EXEC dbo.GetGroupedIntervals @MinuteInterval = 2;
EXEC dbo.GetGroupedIntervals @MinuteInterval = 5;
Run Code Online (Sandbox Code Playgroud)

为简洁起见,我将显示最后一次调用的结果,但您可以与其他人一起玩。

StartDate            c
-------------------  ----
2012-05-16 12:51:00  77
2012-05-16 12:56:00  21
2012-05-16 13:01:00  16
2012-05-16 13:06:00  55
2012-05-16 13:11:00  111
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • 如果基表中的 datetime 列具有索引(或将来可能),则使用 s.dt 的连接可能比使用 datepart 的任何提取方法执行得更好。
  • 我假设您想显示范围内的所有间隔。如果您不想显示计数为 0 的间隔,只需将左外连接更改为内连接。
  • 如果开始日期在转换为 SMALLDATETIME 时四舍五入,我会下降一分钟。对于 1 分钟间隔和可能的其他间隔,这可能导致第一个间隔的计数为 0。您可以调整这种舍入的方式(例如,您可以使用 FLOOR() 来确保它始终向下舍入)。一切都取决于您需要达到的准确程度。
  • 我没有包含任何 WHERE 子句,但您可能需要使用这些子句进行过滤。例如,您的查询可能需要给定日期的所有时间间隔。您可能希望更改 的计算dates以生成当天的所有间隔,而不是当天找到的最小和最大时间之间的所有间隔dbo.SomeTable。以下变体通过显示从午夜开始的一天的数据并按@MinutInterval 递增来处理此问题:

...

CREATE PROCEDURE dbo.GetGroupedIntervalsByDay
    @Date           DATE,
    @MinuteInterval TINYINT = 1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE 
        @IntervalCount INT,
        @StartDate     SMALLDATETIME = @Date;

    SELECT
        @IntervalCount = 1440 / @MinuteInterval;

    ;WITH dates(s,e) AS
    (
        SELECT 
            DATEADD(MINUTE, @MinuteInterval*(n.n-1), @StartDate),
            DATEADD(MINUTE, @MinuteInterval*(n.n),   @StartDate)
        FROM
        (
          SELECT 
            TOP (@IntervalCount) ROW_NUMBER() OVER (ORDER BY o.[object_id])
            FROM sys.all_columns AS o
            ORDER BY o.[object_id]
        ) AS n(n)
    )
    SELECT StartDate = d.s, c = COUNT(s.dt) 
    FROM dates AS d
    LEFT OUTER JOIN dbo.SomeTable AS s
        ON s.dt >= d.s AND s.dt < d.e
        -- AND any filter criteria for dbo.SomeTable?
    GROUP BY d.s
    ORDER BY d.s;
END
GO
Run Code Online (Sandbox Code Playgroud)

示例调用:

EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 1;
EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 2;
EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 5;
Run Code Online (Sandbox Code Playgroud)

上次调用的节略结果:

StartDate             c
-------------------   ----
2012-05-16 00:00:00   0
2012-05-16 00:05:00   0
2012-05-16 00:10:00   0
...
2012-05-16 12:40:00   0
2012-05-16 12:45:00   0
2012-05-16 12:50:00   45
2012-05-16 12:55:00   53
2012-05-16 13:00:00   16
2012-05-16 13:05:00   55
2012-05-16 13:10:00   111
2012-05-16 13:15:00   0
2012-05-16 13:20:00   0
...
2012-05-16 23:45:00   0
2012-05-16 23:50:00   0
2012-05-16 23:55:00   0
Run Code Online (Sandbox Code Playgroud)

(同样,如果您不想包含没有计数的间隔,请将左外连接更改为内连接。如果您选择的间隔不适合 1440,您也可能会发现一些奇怪的结果。我'将把那个案例留给读者作为练习。)