如何获得两行之间的时差?

问题描述 投票:1回答:4

我有一张这样的桌子

Task    Event               Time
2       opened          "2018-12-14 16:23:49.058707+01"
2       closed          "2018-12-14 16:24:49.058707+01"
3       opened          "2018-12-14 16:25:49.058707+01"
3       Interrupted     "2018-12-14 16:26:49.058707+01"
3       closed          "2018-12-14 16:27:49.058707+01"

我需要从表中获取类似的数据

Task    Difference
2           1

仅当仅打开和关闭2个事件时才应获取数据。如果只有2个事件,那么应该采用abs(关闭 - 打开)之间的时差。

我无法弄清楚如何根据事件列找到它

sql postgresql
4个回答
3
投票

这可以使用条件聚合来完成。

select task
      ,max(case when event = 'closed' then time end) - max(case when event = 'opened' then time end) as diff 
--The aggregation can also be expressed using FILTER as shown below
--,max(time) FILTER(where event = 'closed') - max(time) FILTER (where event = 'opened') 
from tbl
group by task
having count(distinct case when event in ('opened','closed') then event end) = 2
and count(distinct event) = 2

1
投票

简化了@VamsiPrabhala的非常好的答案

demo:db<>fiddle

SELECT 
    task,
    MAX(time) - MIN(time) as difference
FROM times
GROUP BY task
HAVING array_agg(event ORDER BY time) = '{"opened","closed"}'
  1. task分组。但只有这些任务只有一个opened和一个closed状态(按此顺序)。通过聚合events来检查
  2. 因为我们知道只有这两个events,按时间排序,第一个(MIN)是opened事件,最后一个(MAX)是closed事件。

此外:

两个时间戳之间的差异总是得到一个interval类型而不是你预期的integer分钟。要获得会议记录,您需要:

EXTRACT(EPOCH FROM
    MAX(time) - MIN(time)
) / 60 as difference

EXTRACT(EPOCH FROM)interval转换为秒。要得到分钟,除以60。


1
投票

Vamsi的解决方案有效,但对我的口味来说太复杂了。我会选择:

select task, 
       max(time) FILTER(where event = 'closed') - max(time) FILTER (where event = 'opened') 
from tbl
group by task
having count(*) = 2 and
       min(event) = 'closed' and
       max(event) = 'opened';

或者,如果我们不想依赖字符串排序:

having count(*) = 2 and
       count(*) filter (where event = 'closed') = 1 and
       count(*) filter (where event = 'opened') = 1 ;

1
投票

另一个选择是将你的表分成3个单独的派生表:一个用于opened事件,一个用于closed事件,一个用于“其他”事件(例如interrupted)。然后,您可以将这些派生表连接在一起以获得所需内容。例如(使用CTE,虽然您当然可以内联查询):

WITH
    -- sample data
    tbl(Task, "Event", Time) AS
    (
        VALUES
        (2, 'opened',       '2018-12-14 16:23:49.058707+01'::TIMESTAMP),
        (2, 'closed',       '2018-12-14 16:24:49.058707+01'::TIMESTAMP),
        (3, 'opened',       '2018-12-14 16:25:49.058707+01'::TIMESTAMP),
        (3, 'interrupted',  '2018-12-14 16:26:49.058707+01'::TIMESTAMP),
        (3, 'closed',       '2018-12-14 16:27:49.058707+01'::TIMESTAMP)
    ),
    -- derived tables
    opened  AS (SELECT * FROM tbl WHERE "Event" = 'opened'),
    closed  AS (SELECT * FROM tbl WHERE "Event" = 'closed'),
    other   AS (SELECT * FROM tbl WHERE "Event" NOT IN ('opened', 'closed'))
SELECT
    -- uses @S-Man's EXTRACT function to get minutes from a TIMESTAMP value.
    ABS(EXTRACT(epoch FROM (opened.Time - closed.Time)) / 60)
FROM opened
    INNER JOIN closed ON
        closed.Task = opened.task
    -- use LEFT JOIN and NULL to exclude records that have an "other" status.
    LEFT JOIN other ON
        other.Task = opened.Task
WHERE other.Task IS NULL
© www.soinside.com 2019 - 2024. All rights reserved.