简体   繁体   中英

how to combine product with Period of validity using LEAD function in oracle?

:)

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?

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;

Demo.

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>

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

or:

SELECT device_id, valid_from, valid_to, product
FROM   table_name
MATCH_RECOGNIZE (
  PARTITION BY device_id
  ORDER BY valid_from
  MEASURES
    FIRST(valid_from) AS valid_from,
    MAX(valid_to) AS valid_to,
    FIRST(product) AS product
  PATTERN ( overlapping* any_row )
  DEFINE
    overlapping AS  MAX(valid_to) >= NEXT(valid_from)
                AND product = NEXT(product)
)

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;

Both output:

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

fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM