SQL:索引/分组具有双重清除条件的事件
Posted
技术标签:
【中文标题】SQL:索引/分组具有双重清除条件的事件【英文标题】:SQL: Indexing/grouping events with dual clear condition 【发布时间】:2014-03-28 14:14:04 【问题描述】:(PostgreSQL 9.3) 我有一个“事件”表,其中包含数百万个复杂事件,存储为设备接收到的。例如目的:
+-----------+-------+
| Timestamp | Event |
+-----------+-------+
| 1 | A |
| 2 | A |
| 2 | B |
| 3 | B |
| 10 | A |
| 11 | A |
| 11 | 0 |
| 11 | C |
| 12 | A |
+-----------+-------+
在这种情况下,我有四种不同类型的事件:A、B、C 和 0。我想做的是对它们进行索引,以便我可以为每个事件设置开始/停止时间戳。停止条件是:在给定的时间戳不再报告事件,或者甚至出现“0”,清除所有事件。最终输出:
+------+----+-------+
| From | To | Event |
+------+----+-------+
| 1 | 3 | A |
| 2 | 10 | B |
| 10 | 11 | A |
| 11 | 11 | C |
| 12 | | A |
+------+----+-------+
在这种情况下,A 在 1 时提出,并在 3 时清除,因为此时不再报告。出于类似的原因,B 在 2 时被加注,在 10 时被清除。 A 在 10 时再次加注,并在 11 时通过 0 事件清除(尽管当时也有报道!)。 C 在 11 提出并同时清除(需要进行一些排序以在同一时间戳处理 0)。最后,A 在 12 时再次引发,并且当前处于活动状态,因此它得到一个 NULL 结束时间戳。
我确实有一些可行的方法,但它需要大量 CTE,因此无法很好地扩展数百万条记录。我一直在尝试 LATERAL(效果很好),我愿意接受任何 9.3 特定的建议。对于这个问题,“事件”本身也被大大简化了,实际上它是一组复杂的列。 Window-functions 也可能在这里应用。
【问题讨论】:
您的第一个示例条目看起来有误,它应该是“to 3”而不是“to 2”。它与所有其他的不一致。 现在没时间写一个正确的答案,但你看看窗口函数是对的。测试event = lag(event) OVER (ORDER BY timestamp)
;这可以让您检测发生变化的边缘。顺便说一句,Stack Overflow 上有很多类似的问题,人们希望生成一个结果集,其中边缘的连续值系列发生变化;找到它们可能有些棘手。
实际上我在第一个索引事件中没有看到“to 2”?但是感谢您查看我的请求 - 我希望您或 Erwin 可以将我推向正确的方向。 Here's a similar question 但不知何故我不知道如何在这里应用相同的逻辑。
【参考方案1】:
在这里开箱即用,为什么不用触发器维护汇总表?
这是您的案例的示例(省略了 FK 等)
create table event_type (
event_type_id serial,
event_name varchar(255)
);
create table event (
event_time timestamp(0),
event_type_id int
);
create table event_summary (
event_summary_id serial,
sum_from timestamp(0),
sum_to timestamp(0),
event_type_id int
);
create language plpgsql;
create or replace function event_insertion() returns trigger as $$
declare
var_event_summary_id integer;
begin
-- find out if event was fired during the previous second
select
event_summary_id
into
var_event_summary_id
from
event_summary s
where
new.event_type_id = s.event_type_id
and sum_to >= new.event_time - interval '1 seconds';
if found then
--update existing summary to include this timestamp
update event_summary set sum_to = new.event_time where event_summary_id = var_event_summary_id;
else
--create new summary for just this timestamp
insert into event_summary(sum_from,sum_to,event_type_id) values (new.event_time,new.event_time,new.event_type_id);
end if;
return null;
end;
$$ language plpgsql;
create trigger event_insertion after insert on event
for each row execute procedure event_insertion();
-- some initial data
insert into event_type(event_name) values ('a');
insert into event_type(event_name) values ('b');
insert into event_type(event_name) values ('c');
insert into event_type(event_name) values ('0');
-- fire the events
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'a'));
select pg_sleep(1);
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'a'));
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'b'));
select pg_sleep(1);
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'b'));
select pg_sleep(7);
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'a'));
select pg_sleep(1);
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'a'));
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = '0'));
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'c'));
select pg_sleep(1);
insert into event(event_time,event_type_id) values (now(),(select event_type_id from event_type where event_name = 'a'));
-- query the summary table
select extract (seconds from s.sum_from), extract (seconds from s.sum_to), t.event_name from event_summary s inner join event_type t on (t.event_type_id = s.event_type_id);
【讨论】:
以上是关于SQL:索引/分组具有双重清除条件的事件的主要内容,如果未能解决你的问题,请参考以下文章