Sql 获取按事件分组的行值之间的时间差

Sql 获取按事件分组的行值之间的时间差,sql,postgresql,Sql,Postgresql,我正在使用Postgres 9.3.3 我有一个包含多个事件的表,其中两个是“可用”和“不可用”。这些事件被指定给特定对象。此表中还有其他对象ID(为清晰起见,已删除): 我需要的是每天的“可用”时间,类似于: 这是部分答案。如果我们假设可用后的下一个事件不可用,则lead()将进行救援,以下是开始: select object_id, to_char(timestamp, 'YYYY-MM-DD') as day, to_char(nextts - timestamp, 'HH

我正在使用Postgres 9.3.3

我有一个包含多个事件的表,其中两个是“可用”和“不可用”。这些事件被指定给特定对象。此表中还有其他对象ID(为清晰起见,已删除):

我需要的是每天的“可用”时间,类似于:
这是部分答案。如果我们假设可用后的下一个事件不可用,则
lead()
将进行救援,以下是开始:

select object_id, to_char(timestamp, 'YYYY-MM-DD') as day,
       to_char(nextts - timestamp, 'HH24:MI') as interval
from (select t.*,
             lead(timestamp) over (partition by object_id order by timestamp) as nextts
      from table t
      where event in ('AVAILABLE', 'UNAVAILABLE')
     ) t
where event = 'AVAILABLE'
group by object_id, to_char(timestamp, 'YYYY-MM-DD');

不过,我怀疑,当间隔跨越多天时,您希望将这些天分割为单独的部分。这变得更具挑战性。

psql输出

 object_id |         day         | available 
-----------+---------------------+-----------
         1 | 1970-01-02 00:00:00 | 12:00:00
         1 | 1970-01-03 00:00:00 | 12:00:00
         1 | 1970-01-04 00:00:00 | 
         1 | 1970-01-05 00:00:00 | 1 day
         1 | 1970-01-06 00:00:00 | 1 day
         1 | 1970-01-07 00:00:00 | 12:00:00
表DDL

create table events (
    object_id int,
    event text,
    timestamp timestamp
);
insert into events (object_id, event, timestamp) values
(1, 'AVAILABLE', '1970-01-02 12:00:00'),
(1, 'UNAVAILABLE', '1970-01-03 12:00:00'),
(1, 'AVAILABLE', '1970-01-05 00:00:00'),
(1, 'UNAVAILABLE', '1970-01-07 12:00:00');

您的示例输出表明您希望返回所有对象,但要分组。如果是这样的话,这个查询可以做到这一点

select object_id, day, sum(upper(tsrange) - lower(tsrange))
from (
  select object_id, date(day) as day, e.tsrange * tsrange(day, day + interval '1' day) tsrange
  from generate_series(timestamp '1970-01-01', '1970-01-07', interval '1' day) day
  left join (
    select object_id,
           case event
             when 'AVAILABLE' then tsrange(timestamp, lead(timestamp) over (partition by object_id order by timestamp))
             else null
           end tsrange
    from events
    where event in ('AVAILABLE', 'UNAVAILABLE')
  ) e on e.tsrange && tsrange(day, day + interval '1' day)
) d
group by object_id, day
order by day, object_id
但这将输出类似的内容(如果您有多个
object\u id
s):

在我看来,如果您一次只查询一个对象,则更有意义:

select day, sum(upper(tsrange) - lower(tsrange))
from (
  select date(day) as day, e.tsrange * tsrange(day, day + interval '1' day) tsrange
  from generate_series(timestamp '1970-01-01', '1970-01-07', interval '1' day) day
  left join (
    select case event
             when 'AVAILABLE' then tsrange(timestamp, lead(timestamp) over (partition by object_id order by timestamp))
             else null
           end tsrange
    from events
    where event in ('AVAILABLE', 'UNAVAILABLE')
    and object_id = 1
  ) e on e.tsrange && tsrange(day, day + interval '1' day)
) d
group by day
order by day
这将输出如下内容:

 day          | sum
--------------+----------
 '1970-01-01' |
 '1970-01-02' | '12:00:00'
 '1970-01-03' | '12:00:00'
 '1970-01-04' |
 '1970-01-05' | '1 day'
 '1970-01-06' | '1 day'
 '1970-01-07' | '12:00:00'
我将此模式/数据用于我的输出:

create table events (
  object_id int,
  event text,
  timestamp timestamp
);

insert into events (object_id, event, timestamp)
values (1, 'AVAILABLE', '1970-01-02 12:00:00'),
       (1, 'UNAVAILABLE', '1970-01-03 12:00:00'),
       (1, 'AVAILABLE', '1970-01-05 00:00:00'),
       (1, 'UNAVAILABLE', '1970-01-07 12:00:00'),
       (2, 'AVAILABLE', '1970-01-06 00:00:00'),
       (2, 'UNAVAILABLE', '1970-01-06 06:00:00'),
       (2, 'AVAILABLE', '1970-01-06 12:00:00'),
       (2, 'UNAVAILABLE', '1970-01-06 18:00:00');

Postgresql版本
select version()
“不过,我怀疑当间隔跨越多天时,您希望将这些天分成不同的部分。”是的,没错,您有日历表吗?不,不幸的是,在繁忙的日子(有多个可用的
时段)不起作用,但不确定OP是否有这样的数据。
 day          | sum
--------------+----------
 '1970-01-01' |
 '1970-01-02' | '12:00:00'
 '1970-01-03' | '12:00:00'
 '1970-01-04' |
 '1970-01-05' | '1 day'
 '1970-01-06' | '1 day'
 '1970-01-07' | '12:00:00'
create table events (
  object_id int,
  event text,
  timestamp timestamp
);

insert into events (object_id, event, timestamp)
values (1, 'AVAILABLE', '1970-01-02 12:00:00'),
       (1, 'UNAVAILABLE', '1970-01-03 12:00:00'),
       (1, 'AVAILABLE', '1970-01-05 00:00:00'),
       (1, 'UNAVAILABLE', '1970-01-07 12:00:00'),
       (2, 'AVAILABLE', '1970-01-06 00:00:00'),
       (2, 'UNAVAILABLE', '1970-01-06 06:00:00'),
       (2, 'AVAILABLE', '1970-01-06 12:00:00'),
       (2, 'UNAVAILABLE', '1970-01-06 18:00:00');