Home > other >  How to return next status following a specific status that has occured
How to return next status following a specific status that has occured

Time:11-09

I'm looking to return the next status after an order has been received, however only looking to capture certain statuses within the following list: 'Shipped', 'Partially shipped', 'Cancelled' Below is the orders table

Order ID Order Status Date
1 Received 1/1/2021
1 Partially Shipped 1/1/2021
1 Returned 1/2/2021
1 Received 1/2/2021
1 Shipped 1/3/2021
2 Received 1/1/2021
2 Acknowledged 1/1/2021
2 Shipped 1/1/2021

As you can see for order ID 2 the following status after received is Acknowledged, but I want to only grab the next status that falls within the following list : 'Shipped', 'Partially shipped', 'Cancelled' Therefore the following output would be correct, skipping acknowledged and returning shipped

Order ID Order Status Date
1 Partially Shipped 1/1/2021
1 Shipped 1/3/2021
2 Shipped 1/1/2021

CodePudding user response:

You can use a conditional LAG analytic statement with the IGNORE NULLS clause:

SELECT Order_id,
       Order_status,
       "DATE"
FROM   (
  SELECT t.*,
         LAG(
           CASE
           WHEN order_status IN ('Received', 'Shipped', 'Partially Shipped', 'Cancelled')
           THEN order_status
           END
         ) IGNORE NULLS OVER (PARTITION BY order_id ORDER BY "DATE") AS prev_status
  FROM   table_name t
)
WHERE  prev_status = 'Received'
AND    order_status IN ('Shipped', 'Partially Shipped', 'Cancelled')

Which, for the sample data:

CREATE TABLE table_name (Order_ID, Order_Status, "DATE") AS
SELECT 1, 'Received',          DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 1, 'Partially Shipped', DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 1, 'Shipped',           DATE '2021-01-02' FROM DUAL UNION ALL
SELECT 1, 'Returned',          DATE '2021-02-01' FROM DUAL UNION ALL
SELECT 1, 'Received',          DATE '2021-02-01' FROM DUAL UNION ALL
SELECT 1, 'Shipped',           DATE '2021-03-01' FROM DUAL UNION ALL
SELECT 2, 'Received',          DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 2, 'Acknowledged',      DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 2, 'Shipped',           DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 3, 'Received',          DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 3, 'Acknowledged',      DATE '2021-02-01' FROM DUAL UNION ALL
SELECT 4, 'Received',          DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 4, 'Acknowledged',      DATE '2021-02-01' FROM DUAL UNION ALL
SELECT 4, 'Received',          DATE '2021-01-01' FROM DUAL;

Outputs:

ORDER_ID ORDER_STATUS DATE
1 Partially Shipped 2021-01-01 00:00:00
1 Shipped 2021-03-01 00:00:00
2 Shipped 2021-01-01 00:00:00

db<>fiddle here

CodePudding user response:

You may use match_recognize for this that looks suitable here, because it can handle repetition of the statuses to be included between. It is available for all supported versions (since 12c).

I've added extra rows to show how it handles, for example, 'Shipped' after 'Partially Shipped', and absence of the next status from the list of required statuses (as for order_id = 3).

with a (Order_ID, Order_Status, dt) as (
  select 1, 'Received', date '2021-01-01'           from dual union all
  select 1, 'Partially Shipped', date '2021-01-01'  from dual union all
  select 1, 'Shipped', date '2021-01-02'           from dual union all
  select 1, 'Returned', date '2021-02-01'           from dual union all
  select 1, 'Received', date '2021-02-01'           from dual union all
  select 1, 'Shipped', date '2021-03-01'           from dual union all
  select 2, 'Received', date '2021-01-01'           from dual union all
  select 2, 'Acknowledged', date '2021-01-01'       from dual union all
  select 2, 'Shipped', date '2021-01-01'           from dual union all
  select 3, 'Received', date '2021-01-01'           from dual union all
  select 3, 'Acknowledged', date '2021-02-01'       from dual union all
  select 3, 'Received', date '2021-03-01'           from dual union all
  select 4, 'Received', date '2021-01-01'           from dual union all
  select 4, 'Acknowledged', date '2021-02-01'       from dual union all
  select 4, 'Received', date '2021-03-01'           from dual union all
  select 4, 'Acknowledged', date '2021-03-02'       from dual union all
  select 4, 'Shipped', date '2021-03-05'       from dual
)
select *
from a
match_recognize (
  partition by order_id
  order by dt asc
  all rows per match
  /*Return the next status after 'Received', that is in a list of interest*/
  pattern (
    {- received -} /*Exclude 'Received' from output*/
    {- exclude* -} /*Exclude "other" statuses after 'Received'*/
    next? /*And include the next status that should be in the list.
            Marked as optional to allow previous exclusion group
            to catch not listed statuses*/
  )
  define
    received as order_status = 'Received',
    /*Other statuses (that are out of interest)*/
    exclude as order_status not in ('Shipped', 'Partially Shipped', 'Cancelled'),
    /*To break match if the next status is not from the list*/
    next as order_status in ('Shipped', 'Partially Shipped', 'Cancelled')
)
ORDER_ID | DT         | ORDER_STATUS
-------: | :--------- | :-----------
       1 | 2021-01-02 | Shipped     
       1 | 2021-03-01 | Shipped     
       2 | 2021-01-01 | Shipped     
       4 | 2021-03-05 | Shipped     

db<>fiddle here

  • Related