:)
How to combine product with Period of validity using LEAD function?
Any idea how i can select device_id, valid_from,valid_to,product in the sense that if the next product / record is the same as the previous one, we take this product for the entire validity period?
DEVICE_ID| VALID_FROM | VALID_TO | PRODUCT
Y0001 01.01.2012 10.01.2012 Y
Y0001 10.01.2012 20.01.2012 Y
Y0001 10.01.2013 20.01.2013 Z
Y0001 21.01.2013 30.01.2013 Y
Y0001 12.02.2013 21.02.2013 X
The expected result of the query I would like to get is as follows:
returns only 4 records : (withhout changing order)
DEVICE_ID| VALID_FROM | VALID_TO | PRODUCT
Y0001 01.01.2012 20.01.2012 Y -- IF next row was the same row - Y then combine period of validity
Y0001 10.01.2013 20.01.2013 Z
Y0001 21.01.2013 30.01.2013 Y
Y0001 12.02.2013 21.02.2013 X
Details:
!*! When next record is the same product, then it should return one product with combine period of validity (taking min valid_from (default value of first row?) and max valid_to of second Y)
Example:
DEVICE_ID| VALID_FROM | VALID_TO | PRODUCT | NEXT_ROW
Y0001 01.01.2012 10.01.2012 Y Y
Y0001 10.01.2012 20.01.2012 Y Z -- NOT Y
Y0001 10.01.2013 20.01.2013 Z 0
Result:
DEVICE_ID| VALID_FROM | VALID_TO | PRODUCT | NEXT_ROW
Y0001 01.01.2012 20.01.2012 Y Z
Y0001 10.01.2013 20.01.2013 Z 0
For now i have start code:
Select device_id
,valid_from
,valid_to
,LEAD(product,1,0) OVER (order by 1) as next_row
from test
and it returns:
DEVICE_ID| VALID_FROM | VALID_TO | PRODUCT | NEXT_ROW
Y0001 01.01.2012 10.01.2012 Y Y
Y0001 10.01.2012 20.01.2012 Y Z
Y0001 10.01.2013 20.01.2013 Z Y
Y0001 21.01.2013 30.01.2013 Y X
Y0001 12.02.2013 21.02.2013 X 0
Is there any way with subquery or something else to return like i show before?
CodePudding user response:
You need set of sub queries to generate the desired result -
with grp_starts as (
select device_id, product, start_date, end_date,
case
when start_date > max(end_date) over(
partition by DEVICE_ID, product order by start_date, end_date
rows between unbounded preceding and 1 preceding
)
then 1 else 0
end grp_start
from DATA
)
, grps as (
select device_id, product, start_date, end_date,
sum(grp_start) over(
partition by device_id, product order by start_date, end_date
) grp
from grp_starts
)
select device_id, product,
min(start_date) start_date,
max(end_date) end_date
from grps
group by device_id, product, grp
order by 3, 4;
CodePudding user response:
To me, it doesn't look like a LEAD
but rather kind of gaps and islands problem.
Sample data:
SQL> with test (device_id, valid_from, valid_to, product) as
2 (select 'Y0001', date '2012-01-01', date '2012-01-10', 'Y' from dual union all
3 select 'Y0001', date '2012-01-10', date '2012-01-20', 'Y' from dual union all
4 select 'Y0001', date '2013-01-10', date '2013-01-20', 'Z' from dual union all
5 select 'Y0001', date '2013-01-21', date '2013-01-30', 'Y' from dual union all
6 select 'Y0001', date '2013-02-12', date '2013-02-21', 'X' from dual
7 ),
Query begins here. First find groups of data (the grp
column):
8 grp as
9 (select device_id, valid_from, valid_to, product,
10 rownum - row_number() over (partition by device_id, product order by valid_From) grp
11 from test
12 )
Finally, fetch MIN and MAX date values per each device, product and group:
13 select device_id,
14 min(valid_from) valid_from,
15 max(valid_to) valid_to,
16 product
17 from grp
18 group by device_id, product, grp
19 order by valid_from;
DEVICE_ID VALID_FROM VALID_TO PRODUCT
---------- ---------- ---------- ----------
Y0001 01.01.2012 20.01.2012 Y
Y0001 10.01.2013 20.01.2013 Z
Y0001 21.01.2013 30.01.2013 Y
Y0001 12.02.2013 21.02.2013 X
SQL>
CodePudding user response:
From Oracle 12, you can solve row-by-row pattern matching problems simply and efficiently using MATCH_RECOGNIZE
:
SELECT device_id, valid_from, valid_to, product
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY device_id, product
ORDER BY valid_from
MEASURES
FIRST(valid_from) AS valid_from,
MAX(valid_to) AS valid_to
PATTERN ( overlapping* any_row )
DEFINE
overlapping AS MAX(valid_to) >= NEXT(valid_from)
)
ORDER BY device_id, valid_from
Which, for the sample data:
CREATE TABLE table_name (DEVICE_ID, VALID_FROM, VALID_TO, PRODUCT) AS
SELECT 'Y0001', DATE '2012-01-01', DATE '2012-01-10', 'Y' FROM DUAL UNION ALL
SELECT 'Y0001', DATE '2012-01-10', DATE '2012-01-20', 'Y' FROM DUAL UNION ALL
SELECT 'Y0001', DATE '2013-01-10', DATE '2013-01-20', 'Z' FROM DUAL UNION ALL
SELECT 'Y0001', DATE '2013-01-21', DATE '2013-01-30', 'Y' FROM DUAL UNION ALL
SELECT 'Y0001', DATE '2013-02-12', DATE '2013-02-21', 'X' FROM DUAL;
Outputs:
DEVICE_ID | VALID_FROM | VALID_TO | PRODUCT |
---|---|---|---|
Y0001 | 2012-01-01 00:00:00 | 2012-01-20 00:00:00 | Y |
Y0001 | 2013-01-10 00:00:00 | 2013-01-20 00:00:00 | Z |
Y0001 | 2013-01-21 00:00:00 | 2013-01-30 00:00:00 | Y |
Y0001 | 2013-02-12 00:00:00 | 2013-02-21 00:00:00 | X |