在 Postgresql 中为每个拒绝或接受日期查找第一个 OPENED_DATE

Find first OPENED_DATE for every Denied or Accepted date in Postgresql

我需要找到请求的第一个文档打开日期。对于每个请求,第一步是打开一个文档,然后可以多次拒绝或接受它。我需要在拒绝日期和接受日期之间找到第一个 open_date。我尝试了 LEAD 函数,但它找到了最近的一个。 我的 table 如下所示:

id      |  document_id | oper_name           |    created_at      |
--------------------------------------------------------------------
24      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 11:40:18 |
25      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 11:40:19 |
27      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 11:40:27 |
28      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 11:40:31 |
29      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 11:42:16 |
30      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 11:45:01 |
31      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 11:48:30 |
32      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 12:34:16 |
33      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 13:12:01 |
34      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 13:42:23 |
35      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 14:40:23 |
36      |  102         | DOCUMENT_IS_ACCEPTED| 2020-07-06 15:48:30 |
37      |  102         | DOCUMENT_IS_OPENED  | 2020-07-06 16:20:45 |
38      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 16:41:30 |

我的结果应该如下所示:

id      |  document_id | oper_name           |    created_at       |  open_date           | parnt_id
28      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 11:40:31 |  2020-07-06 11:40:18 | 24
31      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 11:48:30 |  2020-07-06 11:42:16 | 29
36      |  102         | DOCUMENT_IS_ACCEPTED| 2020-07-06 15:48:30 |  2020-07-06 12:34:16 | 32
38      |  102         | DOCUMENT_IS_DENY    | 2020-07-06 16:41:30 |  2020-07-06 16:20:45 | 37

我尝试了下面的查询,但它并没有像我想要的那样工作

select * from (
select id,document_id,event_type,created_at,
 LEAD(created_at,-1) OVER (ORDER BY created_at asc) as open_date
from table where document_id = 102 order by created_at asc
) s where event_type in ('DOCUMENT_IS_DENY','DOCUMENT_IS_ACCEPTED')

这看起来像是一个缺口和孤岛问题。这是一种方法:

select document_id,
    max(id)        filter(where rn_desc = 1) as id,
    max(oper_name) filter(where rn_desc = 1) as oper_name,
    max(created_at) as created_at,
    min(created_at) as open_date,
    max(id)        filter(where rn_asc = 1) as parent_id
from (
    select t.*,
        row_number() over(partition by document_id, grp order by created_at) rn_asc,
        row_number() over(partition by document_id, grp order by created_at desc) rn_desc
    from (
        select t.*,
            count(*) filter(where oper_name <> 'DOCUMENT_IS_OPENED') over(partition by document_id order by created_at desc) grp
        from mytable t
    ) t
) t
where 1 in (rn_asc, rn_desc)
group by document_id, grp
order by document_id, id

我们的想法是将记录分组(岛屿),其中每个组都以非开场动作结束。我们如何定义组:使用从 table 中的最新日期开始并向后计算的未打开操作计数。然后,我们可以使用window函数来识别每个岛的起点和终点,并通过条件聚合展示我们感兴趣的列。

Demo on DB Fiddle:

document_id | id | oper_name            | created_at          | open_date           | parent_id
----------: | -: | :------------------- | :------------------ | :------------------ | --------:
        102 | 28 | DOCUMENT_IS_DENY     | 2020-07-06 11:40:31 | 2020-07-06 11:40:18 |        24
        102 | 31 | DOCUMENT_IS_DENY     | 2020-07-06 11:48:30 | 2020-07-06 11:42:16 |        29
        102 | 36 | DOCUMENT_IS_ACCEPTED | 2020-07-06 15:48:30 | 2020-07-06 12:34:16 |        32
        102 | 38 | DOCUMENT_IS_DENY     | 2020-07-06 16:41:30 | 2020-07-06 16:20:45 |        37