计算PostgreSQL中匹配条件的行的时间差
Calculate time difference of rows which match condition in PostgreSQL
我有一个 PostgreSQL table,我需要计算一个名字处于状态 1 的时间间隔。一个名字可以多次处于这个状态,我需要每个单独间隔的时间。我正在使用临时 table 保存间隔的起点和终点,然后计算它的时差。但我不喜欢这种解决方案,并且认为必须有更好的方法来实现它。我希望能在这里找到一些 SQL 专家,他们可以向我展示一些神奇而简单的解决方案。
下面是 table 的样子:
|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 示例脚本 table:
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');
这是一种间隙和孤岛问题。在这种情况下,行号的差异应该做你想要的:
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 是一个 db<>fiddle.
行号差异的逻辑解释起来有点棘手。如果您 运行 子查询,您将看到具有相同名称和相邻值 state
的行的行号差异是如何保持不变的。聚合只是按差值聚合,差值对他们来说是常数。
我有一个 PostgreSQL table,我需要计算一个名字处于状态 1 的时间间隔。一个名字可以多次处于这个状态,我需要每个单独间隔的时间。我正在使用临时 table 保存间隔的起点和终点,然后计算它的时差。但我不喜欢这种解决方案,并且认为必须有更好的方法来实现它。我希望能在这里找到一些 SQL 专家,他们可以向我展示一些神奇而简单的解决方案。
下面是 table 的样子:
|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 示例脚本 table:
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');
这是一种间隙和孤岛问题。在这种情况下,行号的差异应该做你想要的:
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 是一个 db<>fiddle.
行号差异的逻辑解释起来有点棘手。如果您 运行 子查询,您将看到具有相同名称和相邻值 state
的行的行号差异是如何保持不变的。聚合只是按差值聚合,差值对他们来说是常数。