【问题标题】:Calculate time difference of rows which match condition in PostgreSQL计算PostgreSQL中匹配条件的行的时间差
【发布时间】:2021-06-25 19:23:00
【问题描述】:

我有一个 PostgreSQL 表,我需要在其中计算名称处于状态 1 的时间间隔。一个名称可以多次处于此状态,我需要每个单独间隔的时间。我正在使用一个临时表,其中我保存了一个间隔的开始和结束点,然后计算它的时间差。但我不喜欢这种解决方案,并认为必须有更好的方法。我希望在这里找到一些 SQL 专业人士,他们可以向我展示一些神奇而简单的解决方案。

表格如下所示:

|name   |state |time                      |
|-------|------|--------------------------|
|one    | 1    |'2020-11-11 01:00:02.5+01'| (start of first interval)
|one    | 1    |'2020-11-11 01:00:04.5+01'| (end of first interval) = 2 seconds
|one    | 0    |'2020-11-11 01:00:05.0+01'|
|one    | 0    |'2020-11-11 01:00:05.5+01'|
|one    | 1    |'2020-11-11 01:00:10.5+01'| (start of second interval)
|one    | 1    |'2020-11-11 01:00:11.5+01'| 
|one    | 1    |'2020-11-11 01:00:12.5+01'| (end of second interval) = 2 seconds
|two    | 0    |'2020-11-11 01:00:13.0+01'|
|two    | 0    |'2020-11-11 01:00:14.5+01'|
|two    | 1    |'2020-11-11 01:00:15.0+01'| (start of third interval)
|two    | 1    |'2020-11-11 01:00:15.5+01'| (end of third interval) = 0.5 seconds
|two    | 0    |'2020-11-11 01:00:16.5+01'|

示例表的 SQL 脚本:

CREATE TABLE intervals(
    name char(10),
    state integer,
    time timestamptz
);

INSERT INTO intervals(name, state, time) VALUES
    ('one', 1, '2020-11-11 01:00:02.5+01'),
    ('one', 1, '2020-11-11 01:00:04.5+01'),
    ('one', 0, '2020-11-11 01:00:05.0+01'),
    ('one', 0, '2020-11-11 01:00:05.5+01'),
    ('one', 1, '2020-11-11 01:00:10.5+01'),
    ('one', 1, '2020-11-11 01:00:11.5+01'),
    ('one', 1, '2020-11-11 01:00:12.5+01'),
    ('two', 0, '2020-11-11 01:00:13.0+01'),
    ('two', 0, '2020-11-11 01:00:14.5+01'),
    ('two', 1, '2020-11-11 01:00:15.0+01'),
    ('two', 1, '2020-11-11 01:00:15.5+01'),
    ('two', 0, '2020-11-11 01:00:16.5+01');

【问题讨论】:

    标签: sql postgresql aggregate-functions window-functions


    【解决方案1】:

    这是一种孤岛问题。在这种情况下,行号的差异应该做你想要的:

    select name, min(time), max(time),
           max(time) - min(time) as duration
    from (select i.*,
                 row_number() over (partition by name order by time) as seqnum,
                 row_number() over (partition by name, state order by time) as seqnum_2
          from intervals i
         ) i
    where state = 1
    group by name, (seqnum - seqnum_2), state;
    

    Here 是一个 dbfiddle。

    关于行号差异的逻辑有点难以解释。如果您运行子查询,您将看到state 具有相同名称和相邻值的行上的行号差异是恒定的。聚合只是按差异进行聚合,这对他们来说是恒定的。

    【讨论】:

    • 非常感谢!这个解决方案很棒而且很整洁。但我完全不明白。 seqnum 和 seqnum_2 如何与 min() 和 max() 结合使用?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-24
    • 2020-10-25
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    相关资源
    最近更新 更多