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

时间:2018-12-20 14:11:09

标签: sql postgresql

我有一张这样的桌子

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个事件,则应采用abs(关闭-打开)之间的时差。

我无法根据“事件”列弄清楚如何做

4 个答案:

答案 0 :(得分: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 :(得分:1)

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

demo:db<>fiddle

task
  1. opened分组。但是,只有这些任务恰好具有一个closed和一个event状态(按此顺序)。通过汇总event s
  2. 进行检查
  3. 因为我们知道只有两个MIN,按时间排序,所以第一个(opened)是MAX事件,最后一个({{1} })是closed事件。

此外:

两个时间戳之间的时差始终为interval类型,而不是预期的integer分钟。要获取会议记录,您需要:

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

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

答案 2 :(得分: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 ;

答案 3 :(得分:1)

但是另一个选项是将表分为3个独立的派生表:一个用于opened事件,一个用于closed事件,另一个用于“ other”事件(例如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