Oracle SQL lead lag across different group

这一生的挚爱 提交于 2021-02-08 08:30:31

问题


Suppose I have the below table. They key is just concat P1, P2, P3. I want to compare between key for each day. for example, from day 1 to day2, abc is removed and abe, aby is added.

P1  P2  P3  DAY KEY

a   b   c   1   abc
a   b   e   2   abe
a   b   y   2   aby
a   b   x   3   abx
a   b   c   3   abc

Expected result set:

KEY OPERATION DAY

abc  ADD      1
abe  ADD      2
aby  ADD      2
abc  REMOVE   2
abx  ADD      3
abc  ADD      3
abe  REMOVE   3
aby  REMOVE   3

And what if the day is not sequential. For example:

P1  P2  P3  DAY  KEY

a   b   c   1    abc
a   b   e   2    abe
a   b   y   2    aby
a   b   x   5    abx
a   b   c   5    abc

And the expected result is:

KEY OPERATION DAY

abc  ADD      1
abe  ADD      2
aby  ADD      2
abc  REMOVE   2
abx  ADD      5
abc  ADD      5
abe  REMOVE   5
aby  REMOVE   5

回答1:


Here is an approach using lag() and lead(). The idea is to compare the previous and following date value for each key, and use that to identify the date when a key was added or removed - this assumes that day is a number that increments sequentially, without gaps.

with cte as (
    select
        t.*,
        lag(day) over(partition by p1, p2, p3 order by day) lag_day,
        lead(day) over(partition by p1, p2, p3 order by day) lead_day
    from mytable t
)
select p1, p2, p3, day, 'add' event
from cte
where lag_day is null or lag_day <> day - 1 
union all
select p1, p2, p3, day + 1, 'remove'
from cte
where lead_day is null or lead_day <> day + 1 
order by day, p1, p2, p3

With your sample data in this db fiddle, this produces:

P1 | P2 | P3 | DAY | EVENT 
:- | :- | :- | --: | :-----
a  | b  | c  |   1 | add   
a  | b  | c  |   2 | remove
a  | b  | e  |   2 | add   
a  | b  | y  |   2 | add   
a  | b  | c  |   3 | add   
a  | b  | e  |   3 | remove
a  | b  | x  |   3 | add   
a  | b  | y  |   3 | remove
a  | b  | c  |   4 | remove
a  | b  | x  |   4 | remove

This seems more complete that your expected results. Note that all records still available on the last date appear as removed on the following (hypothetical) day - this seems consistent with the fact that all new records appear as added on the first day.

I did not use the pseudo-key, because it don't see how it helps - and, if the values had more than one character, it could lead into trouble by creating "false" duplicates.




回答2:


You can do this without window functions, if you want:

select key, 'add', day
from t
where not exists (select 1
                  from t t2
                  where t2.key = t.key and t2.day = t.day - 1
                 )
union all
select key, 'remove', day + 1
from t
where not exists (select 1
                  from t t2
                  where t2.key = t.key and t2.day = t.day + 1
                 )


来源:https://stackoverflow.com/questions/62121770/oracle-sql-lead-lag-across-different-group

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!