问题
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