tonirush February 2016

sql-query that change all validTo dates to the next validFrom date minus one Day

I have to modify a big pricelist table so that there is only one valid price for every article. Sometimes the sales employees insert new prices and forgot to change the old infinite validTo dates.

So I have to write a sql-query to change all validTo dates to the next validFrom date minus one day, when the validTo date has infinite validity (9999-12-31). But I have no idea how can i reach this with only SQL (Oracle 12).

anr price   validFrom   validTo
1   447.1     2015-06-01  9999-12-31 <
1   447.2       2015-06-16  2015-06-16
1   447.3       2015-06-17  2015-06-17
1   447.4       2015-06-22  2015-06-22
1   447.5       2015-07-06  9999-12-31 <
1   395.0       2015-07-20  2015-07-20
1   447.6       2015-08-03  9999-12-31 <
1   447.7       2015-08-17  9999-12-31 <
1   447.8       2015-08-24  9999-12-31 <
1   395.0       2015-09-07  2015-09-07
1   450.9       2015-11-15  9999-12-31 < no change because it is the last entry

after updating the the table, the result should look like

anr price   validFrom   validTo
1   447.1       2015-06-01  2015-06-15 <
1   447.2       2015-06-16  2015-06-16
1   447.3       2015-06-17  2015-06-17
1   447.4       2015-06-22  2015-06-22
1   447.5       2015-07-06  2015-07-19 <
1   395.0       2015-07-20  2015-07-20
1   447.6       2015-08-03  2015-08-16 <
1   447.7       2015-08-17  2015-08-23 <
1   447.8       2015-08-24  2015-09-06 <
1   395.0       2015-09-07  2015-09-07
1   450.9       2015-11-15  9999-12-31 <

Answers


Thorsten Kettner February 2016

There are two possibilities:

1. Explicit time spans

price   validFrom   validTo
90.99   2016-01-01  9999-12-31
80.00   2016-01-16  2016-01-17

The first price would be valid both before January 16 and after January 17, whereas the second price was only valid on two days in January.

It would then be a very bad idea to change the first validTo.

2. Implicit time spans

price   validFrom 
90.99   2016-01-01
80.00   2016-01-16
90.99   2016-01-18

This data represents the same as in the explicit time spans example. The first price is valid before January 16, then the second price is valid until January 17, and afterwards the next price (which equals the first price again) is valid. Here you don't need an EndDate, because it's implicit. Of course the first price is only valid until January 15, because from January 16 there is another price valid (record #2).

So: Either remove the EndDate column completely or let it untouched. Don't simply update it, as you have intended. If you updated your records to next date minus one, you would actually hold data redundantly, which might lead to problems later.


collapsar February 2016

Oracle provides an analytic function LEAD that references the current-plus-n-th record given a sort criterion. This function may serve the purpose of selecting the proper date value in an update statement as follows ( let test_prices be the table name, ppk its PK ):

    update test_prices p
       set p.validTo = (
                        select ps.vtn
                          from (
                                   select lead ( p1.validFrom, 1 ) over ( order by p1.validFrom )  - 1    vtn
                                        , ppk
                                     from test_prices p1
                               ) ps
                         where ps.ppk = p.ppk
                     )
     where to_char(p.validTo, 'YYYY') = '9999'
       and p.validFrom != ( select max(validFrom) from test_prices )
         ;                       


MT0 February 2016

UPDATE VALID_DATES v
SET    validTo = (
  SELECT validTo
  FROM   (
    SELECT anr,
           validFrom,
           COALESCE(
             LEAD( validFrom - 1, 1 ) OVER ( PARTITION BY anr ORDER BY validFrom ),
             validTo
           ) AS validTo
    FROM   valid_dates
  ) u
  WHERE  v.anr       = u.anr
  AND    v.validFrom = u.validFrom
)
WHERE validTo = DATE '9999-12-31';


Thorsten Kettner February 2016

In order to update an end date you can simply select the minimum of all higher start dates.

update mytable upd
set enddate = coalesce(
(
  select min(startdate) - 1
  from mytable later
  where later.startdate > upd.startdate
  and later.anr = upd.anr -- same product
), date'9999-12-31') -- coalesce for the case there is no later record
where enddate = date'9999-12-31';

I have taken anr to be the product id. If it isn't then change the statement accordingly.

Post Status

Asked in February 2016
Viewed 2,933 times
Voted 6
Answered 4 times

Search




Leave an answer