Skip to content
Advertisement

Find first OPENED_DATE for every Denied or Accepted date in Postgresql

I need to find first document open date for request. For every request first step is opening a document and then it can be denied or accepted multiple times. I need to find first open_date between deny and accept dates. I tried LEAD function but it finds nearest one. My table looks like below:

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 |

My result should look like below:

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

I tried below query but it does not work like I want

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

Advertisement

Answer

This reads like a gaps-and-islands problem. Here is one approach:

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

The idea is to put records in groups (the islands), where each group ends with a non-opening action. How do we define the groups: with a count of non-opening actions that starts from the latest date in the table and goes backwards. Then, we can use window funtions to identify the start and end of each island, and exhibit the columns we are interested it with conditional aggregation.

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
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement