【问题标题】:Partition by time-interval按时间间隔分区
【发布时间】:2018-09-24 15:14:04
【问题描述】:

我正在寻找按日期时间值在分区上运行窗口函数的最佳方法。但是,我希望按日期时间进行分区,而不是按确切时间进行分区,例如彼此相隔 15 分钟之内。

这是我桌子的一小块。

CREATE TABLE my_table(ID VARCHAR(5), in_time DATETIME)

INSERT INTO my_table (ID, in_time) VALUES
('4844', '2017-04-06 10:15:00.000'),
('5221', '2017-11-24 11:18:00.000'),
('5221', '2017-11-24 11:18:00.000'),
('5221', '2017-11-25 14:23:00.000'),
('8486', '2017-10-10 15:30:00.000'),
('8486', '2017-10-10 15:32:00.000'),
('8486', '2017-10-10 15:46:00.000'), -- new row after updating question
('8486', '2017-10-10 16:00:00.000') -- new row after updating question

这是我现在使用的查询:

SELECT *, 
    ROW_NUMBER() OVER(PARTITION BY ID, in_time ORDER BY ID, in_time) AS filter_row
FROM my_table

正如预期的那样:

ID      in_time                     filter_row
4844    2017-04-06 10:15:00.000     1
5221    2017-11-24 11:18:00.000     1
5221    2017-11-24 11:18:00.000     2
5221    2017-11-25 14:23:00.000     1
8486    2017-10-10 15:30:00.000     1
8486    2017-10-10 15:32:00.000     1
8486    2017-10-10 15:46:00.000     1
8486    2017-10-10 16:00:00.000     1

我想要实现的是:

ID      in_time                     filter_row
4844    2017-04-06 10:15:00.000     1
5221    2017-11-24 11:18:00.000     1
5221    2017-11-24 11:18:00.000     2
5221    2017-11-25 14:23:00.000     1
8486    2017-10-10 15:30:00.000     1
8486    2017-10-10 15:32:00.000     2 -- < notice the 2 here
8486    2017-10-10 15:46:00.000     3 -- < notice the 3 here
8486    2017-10-10 16:00:00.000     4 -- < notice the 4 here

正如您在上面看到的那样,ID = 8486 的行应该被分区在一起,因为它们各自的in_time 和上面一行的in_time 之间只有 2、14 和 14 分钟。如何有效地做到这一点?

【问题讨论】:

    标签: sql sql-server datetime window-functions


    【解决方案1】:

    以下示例通过根据指定的间隔(以分钟为单位)计算间隔开始时间并按该值进行分区来提供所需的结果。

    DECLARE @IntervalMinutes int = 15;
    SELECT *, 
        ROW_NUMBER() OVER(
              PARTITION BY ID
            , (DATEADD(minute, (DATEDIFF(minute, '', in_time)/@IntervalMinutes)*@IntervalMinutes, '')
            )
            ORDER BY ID, in_time) AS filter_row
    FROM my_table;
    

    编辑:

    上面的代码计算了固定长度的间隔。您可以通过ID 识别超出所需间隔的岛屿来解决您更新的问题。下面的方法使用NOT EXISTSCROSS APPLY来识别这些岛屿,并确定每个岛屿的间隔开始和结束时间。

    DECLARE @IntervalMinutes int = 15;
    WITH
        start_intervals AS (
            SELECT DISTINCT
                  ID
                , in_time
            FROM dbo.my_table AS a
            WHERE NOT EXISTS(
                SELECT 1
                FROM dbo.my_table AS b
                WHERE
                    b.ID = a.ID
                    AND b.in_time < a.in_time
                    AND b.in_time > DATEADD(minute, -@IntervalMinutes, a.in_time)
                )
            )
        , end_intervals AS (
            SELECT
                  ID
                , in_time
            FROM dbo.my_table AS a
            WHERE NOT EXISTS(
                SELECT 1
                FROM dbo.my_table AS b
                WHERE
                    b.ID = a.ID
                    AND b.in_time > a.in_time
                    AND b.in_time < DATEADD(minute, @IntervalMinutes, a.in_time)
                )
        )
        , intervals AS (
            SELECT
                  ID
                , start_intervals.in_time AS start_interval
                , end_intervals.in_time AS end_interval
            FROM start_intervals
            CROSS APPLY(
                SELECT TOP(1) in_time
                FROM end_intervals 
                WHERE
                    end_intervals.ID = start_intervals.ID
                    AND end_intervals.in_time >= start_intervals.in_time
                ) AS end_intervals
            )
    SELECT 
          my_table.ID
        , my_table.in_time
        , ROW_NUMBER() OVER(PARTITION BY my_table.ID, intervals.start_interval ORDER BY(intervals.start_interval)) AS filter_row
    FROM dbo.my_table
    JOIN intervals ON my_table.in_time BETWEEN intervals.start_interval AND intervals.end_interval
    

    【讨论】:

    • 绝招! =D 我只想添加这个很酷的技巧,即始终将时间固定在“时间分区的下端”,这意味着例如 2017-11-24 11:29:59.999 将转换为 2017-11-24 11:15:00.000
    • 这显然不是条件的答案我希望按日期时间进行分区,例如彼此相隔 15 分钟之内。 请参阅DbFiddle.
    • @kiln,如果有人从字面上解释这个问题,那么同意。我的假设是需要固定的非重叠间隔。
    • 我总是按字面意思解释问题。如果可能的话,当然可以。
    • 我觉得我接受这个答案有点太快了。如果我正确理解您的解决方案,如果日期在同一组固定的 15 分钟间隔内,它会划分日期吗? IE。 00:00-00:14、00:15-00:29等
    猜你喜欢
    • 2016-03-05
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 2011-02-08
    • 2019-06-28
    • 2018-10-12
    • 1970-01-01
    • 2016-10-06
    相关资源
    最近更新 更多