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

Posted

技术标签:

【中文标题】在 Postgresql 中为每个被拒绝或接受的日期查找第一个 OPENED_DATE【英文标题】:Find first OPENED_DATE for every Denied or Accepted date in Postgresql 【发布时间】:2021-02-27 13:12:03 【问题描述】:

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

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')

【问题讨论】:

【参考方案1】:

这听上去像是一个孤岛问题。这是一种方法:

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

这个想法是将记录放在组(岛屿)中,其中每个组以非打开操作结束。我们如何定义组:使用从表中的最新日期开始并倒退的非打开操作计数。然后,我们可以使用窗口函数来识别每个岛的起点和终点,并通过条件聚合展示我们感兴趣的列。

Demo on DB Fiddle

文档 ID |编号 |操作名称 | created_at |开放日期 | 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

【讨论】:

非常感谢它工作正常。从未听说过gaps-and-islands problem

以上是关于在 Postgresql 中为每个被拒绝或接受的日期查找第一个 OPENED_DATE的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 连接被拒绝

Paypal 商家接受或拒绝每笔交易

如何在C#中接受或拒绝 Excel 中的修订

在 postgresql 中为每个更新的行调用一个函数

如何确定 XMPP 中接受或拒绝联系请求?

i psql 权限被拒绝