oracle database - analytic functions - advanced cases

82
Analytic Functions Advanced Cases Kim Berg Hansen T. Hansen Gruppen A/S

Upload: kim-berg-hansen

Post on 11-May-2015

488 views

Category:

Data & Analytics


1 download

DESCRIPTION

Using Oracle SQL Analytic functions Case: Picking by FIFO (like my previous presentations) Case: Sales forecasting (Time Series Analysis - new case) As presented on ODTUG Kscope14 conference

TRANSCRIPT

Page 1: Oracle database - Analytic functions - Advanced cases

Analytic FunctionsAdvanced Cases

Kim Berg Hansen

T. Hansen Gruppen A/S

Page 2: Oracle database - Analytic functions - Advanced cases

Picking by FIFO

Sales forecasting

Cases

Page 3: Oracle database - Analytic functions - Advanced cases

FIFO – First-In-First-Out principle● Pick oldest items first

Picking route● Don’t drive back and forth through the aisles of the

warehouse

Single SQL● Utilize the power of the database

Case: Picking by FIFO

Page 4: Oracle database - Analytic functions - Advanced cases

Warehouses

Page 5: Oracle database - Analytic functions - Advanced cases

create table inventory ( item varchar2(10) -- identification of the item , loc varchar2(10) -- identification of the location , qty number -- quantity present at that location , purch date -- date that quantity was purchased);

insert into inventory values('Ale' , '1-A-20', 18, DATE '2014-02-01');insert into inventory values('Ale' , '1-A-31', 12, DATE '2014-02-05');insert into inventory values('Ale' , '1-C-05', 18, DATE '2014-02-03');insert into inventory values('Ale' , '2-A-02', 24, DATE '2014-02-02');insert into inventory values('Ale' , '2-D-07', 9, DATE '2014-02-04');insert into inventory values('Bock', '1-A-02', 18, DATE '2014-02-06');insert into inventory values('Bock', '1-B-11', 4, DATE '2014-02-05');insert into inventory values('Bock', '1-C-04', 12, DATE '2014-02-03');insert into inventory values('Bock', '1-B-15', 2, DATE '2014-02-02');insert into inventory values('Bock', '2-D-23', 1, DATE '2014-02-04');

Inventoryloc = 1-A-201 = warehouseA = aisle20 = position

Page 6: Oracle database - Analytic functions - Advanced cases

create table orderline ( ordno number -- id-number of the order , item varchar2(10) -- identification of the item , qty number -- quantity ordered);

insert into orderline values (42, 'Ale' , 24);insert into orderline values (42, 'Bock', 18);

Order

One order24 Ale18 Bock

Page 7: Oracle database - Analytic functions - Advanced cases

Join upselect o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 order by o.item, i.purch, i.loc;

ITEM ORD_QTY LOC PURCH LOC_QTY----- ------- ------- ---------- -------Ale 24 1-A-20 2014-02-01 18Ale 24 2-A-02 2014-02-02 24Ale 24 1-C-05 2014-02-03 18Ale 24 2-D-07 2014-02-04 9Ale 24 1-A-31 2014-02-05 12Bock 18 1-B-15 2014-02-02 2Bock 18 1-C-04 2014-02-03 12Bock 18 2-D-23 2014-02-04 1Bock 18 1-B-11 2014-02-05 4Bock 18 1-A-02 2014-02-06 18

Order locations for each item by purchase dateVisually easy to see what we need to pick18 Ale of the oldest and 6 of the next and so on

Page 8: Oracle database - Analytic functions - Advanced cases

select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 order by o.item, i.purch, i.loc;

Rolling sum of previous

If the sum of all previous rows is greater than or equal to the ordered quantity, we have picked sufficient and can stop

Analytic sumPartition for each itemOrder by dateRolling sum of all previous rows

Page 9: Oracle database - Analytic functions - Advanced cases

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY----- ------- ------- ---------- ------- -----------Ale 24 1-A-20 2014-02-01 18Ale 24 2-A-02 2014-02-02 24 18Ale 24 1-C-05 2014-02-03 18 42Ale 24 2-D-07 2014-02-04 9 60Ale 24 1-A-31 2014-02-05 12 69Bock 18 1-B-15 2014-02-02 2Bock 18 1-C-04 2014-02-03 12 2Bock 18 2-D-23 2014-02-04 1 14Bock 18 1-B-11 2014-02-05 4 15Bock 18 1-A-02 2014-02-06 18 19

Rolling sum of previous

Each row can now evaluate if sufficient has been pickedIf the sum of all previous rows is less than the ordered quantity we still need to pick something and the row is needed

Page 10: Oracle database - Analytic functions - Advanced cases

select s.* , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42

) s where s.sum_prv_qty < s.ord_qty order by s.item, s.purch, s.loc;

Filter on previous

Keep only rows where we still need something to pick

Pick location quantity or what is still needed, whichever is smallest

Set NULL in first row of partition to 0 otherwise predicate will fail

Page 11: Oracle database - Analytic functions - Advanced cases

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY PICK_QTY----- ------- ------- ---------- ------- ----------- --------Ale 24 1-A-20 2014-02-01 18 0 18Ale 24 2-A-02 2014-02-02 24 18 6Bock 18 1-B-15 2014-02-02 2 0 2Bock 18 1-C-04 2014-02-03 12 2 12Bock 18 2-D-23 2014-02-04 1 14 1Bock 18 1-B-11 2014-02-05 4 15 3

Filter on previous

We have now selected the necessary inventory quantities to fulfil the order and pick the oldest items first (FIFO)

Page 12: Oracle database - Analytic functions - Advanced cases

Picklist – FIFOselect s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty order by s.loc;

LOC ITEM PICK_QTY------- ----- --------1-A-20 Ale 181-B-11 Bock 31-B-15 Bock 21-C-04 Bock 122-A-02 Ale 62-D-23 Bock 1

Simple FIFO picklistItem and quantity to pickBy location order

Page 13: Oracle database - Analytic functions - Advanced cases

Picklist – Shortest routeselect s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.loc -- << only line changed rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty order by s.loc;

LOC ITEM PICK_QTY------- ----- --------1-A-02 Bock 181-A-20 Ale 181-A-31 Ale 6

Switch picking strategy =Switch analytic order byKeep "outer" order by

Page 14: Oracle database - Analytic functions - Advanced cases

Picklist – Least number of picksselect s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty desc, i.loc -- << only line changed rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty order by s.loc;

LOC ITEM PICK_QTY------- ----- --------1-A-02 Bock 182-A-02 Ale 24

Switch picking strategy =Switch analytic order byKeep "outer" order by

Page 15: Oracle database - Analytic functions - Advanced cases

Picklist – Clean out small quantitiesselect s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty, i.loc -- << only line changed rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty order by s.loc;

LOC ITEM PICK_QTY------- ----- --------1-A-20 Ale 31-A-31 Ale 121-B-11 Bock 41-B-15 Bock 21-C-04 Bock 112-D-07 Ale 92-D-23 Bock 1

Switch picking strategy =Switch analytic order byKeep "outer" order by

Page 16: Oracle database - Analytic functions - Advanced cases

Not the greatest picking route

Strategy "Clean out small quantities" give most number of picksWe use that for picking route demonstration

Page 17: Oracle database - Analytic functions - Advanced cases

select to_number(substr(s.loc,1,1)) warehouse , substr(s.loc,3,1) aisle , to_number(substr(s.loc,5,2)) position , s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item , o.qty ord_qty , i.loc , i.purch , i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42

) s where s.sum_prv_qty < s.ord_qty order by s.loc;

Warehouse, aisle and position

Split location in parts- Warehouse- Aisle- Position

Page 18: Oracle database - Analytic functions - Advanced cases

WAREHOUSE AISLE POSITION LOC ITEM PICK_QTY--------- ----- -------- ------- ----- -------- 1 A 20 1-A-20 Ale 3 1 A 31 1-A-31 Ale 12 1 B 11 1-B-11 Bock 4 1 B 15 1-B-15 Bock 2 1 C 4 1-C-04 Bock 11 2 D 7 2-D-07 Ale 9 2 D 23 2-D-23 Bock 1

Warehouse, aisle and position

Warehouse, aisle and position might be from lookup tables instead – here is simple substr for demonstration purposes

Page 19: Oracle database - Analytic functions - Advanced cases

select to_number(substr(s.loc,1,1)) warehouse , substr(s.loc,3,1) aisle , dense_rank() over ( order by to_number(substr(s.loc,1,1)) -- warehouse , substr(s.loc,3,1) -- aisle ) aisle_no , to_number(substr(s.loc,5,2)) position , s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42

) s

where s.sum_prv_qty < s.ord_qty order by s.loc;

Consecutive numbering of aisles

Dense rank gives equal rank to rows with same values in the order by and each rank is one higher than the previous

Page 20: Oracle database - Analytic functions - Advanced cases

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY--------- ----- -------- -------- ------- ----- -------- 1 A 1 20 1-A-20 Ale 3 1 A 1 31 1-A-31 Ale 12 1 B 2 11 1-B-11 Bock 4 1 B 2 15 1-B-15 Bock 2 1 C 3 4 1-C-04 Bock 11 2 D 4 7 2-D-07 Ale 9 2 D 4 23 2-D-23 Bock 1

Consecutive numbering of aisles

Aisles get consecutive numbering in the order they are visited

Page 21: Oracle database - Analytic functions - Advanced cases

select s2.warehouse, s2.aisle, s2.aisle_no, s2.position , s2.loc, s2.item, s2.pick_qty from ( select to_number(substr(s.loc,1,1)) warehouse , substr(s.loc,3,1) aisle , dense_rank() over ( order by to_number(substr(s.loc,1,1)) -- warehouse , substr(s.loc,3,1) -- aisle ) aisle_no , to_number(substr(s.loc,5,2)) position , s.loc, s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty

) s2 order by s2.warehouse , s2.aisle_no , case when mod(s2.aisle_no,2) = 1 then s2.position else -s2.position end;

Odd / even ordering

We order the positions in "odd" aisles "upward" and in "even" aisles "downward"

Page 22: Oracle database - Analytic functions - Advanced cases

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY--------- ----- -------- -------- ------- ----- -------- 1 A 1 20 1-A-20 Ale 3 1 A 1 31 1-A-31 Ale 12 1 B 2 15 1-B-15 Bock 2 1 B 2 11 1-B-11 Bock 4 1 C 3 4 1-C-04 Bock 11 2 D 4 23 2-D-23 Bock 1 2 D 4 7 2-D-07 Ale 9

Odd / even ordering

The desired ordering – the first aisle by position ascending – the second aisle by position descending – and so on

Page 23: Oracle database - Analytic functions - Advanced cases

Much better picking route

Page 24: Oracle database - Analytic functions - Advanced cases

select s2.warehouse, s2.aisle, s2.aisle_no, s2.position , s2.loc, s2.item, s2.pick_qty from ( select to_number(substr(s.loc,1,1)) warehouse , substr(s.loc,3,1) aisle , dense_rank() over ( partition by to_number(substr(s.loc,1,1)) -- warehouse order by substr(s.loc,3,1) -- aisle ) aisle_no , to_number(substr(s.loc,5,2)) position , s.loc, s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.qty, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderline o join inventory i on i.item = o.item where o.ordno = 42 ) s where s.sum_prv_qty < s.ord_qty ) s2 order by s2.warehouse , s2.aisle_no , case when mod(s2.aisle_no,2) = 1 then s2.position else -s2.position end;

Restart count if only one door

Partition by warehouse

Page 25: Oracle database - Analytic functions - Advanced cases

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY--------- ----- -------- -------- ------- ----- -------- 1 A 1 20 1-A-20 Ale 3 1 A 1 31 1-A-31 Ale 12 1 B 2 15 1-B-15 Bock 2 1 B 2 11 1-B-11 Bock 4 1 C 3 4 1-C-04 Bock 11 2 D 1 7 2-D-07 Ale 9 2 D 1 23 2-D-23 Bock 1

Restart count if only one door

Warehouse change restarts the aisle_no counterSo the first aisle in each warehouse starts by 1and therefore is odd and positions ordered ascending

Page 26: Oracle database - Analytic functions - Advanced cases

Restart count if only one door

Page 27: Oracle database - Analytic functions - Advanced cases

delete orderline;

insert into orderline values (51, 'Ale' , 24);insert into orderline values (51, 'Bock', 18);insert into orderline values (62, 'Ale' , 8);insert into orderline values (73, 'Ale' , 16);insert into orderline values (73, 'Bock', 6);

Batch pick multiple orders

Get rid of the first test order and insert three orders of various beers

Page 28: Oracle database - Analytic functions - Advanced cases

with orderbatch as ( select o.item , sum(o.qty) qty from orderline o where o.ordno in (51, 62, 73) group by o.item)select s.loc, s.item, least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty from orderbatch o join inventory i on i.item = o.item ) s where s.sum_prv_qty < s.ord_qty order by s.loc;

Aggregate orders by item

Named subquery that is the sum of ordered quantities by item

Use subquery in FIFO query instead of orderline table

Page 29: Oracle database - Analytic functions - Advanced cases

LOC ITEM PICK_QTY------- ----- --------1-A-02 Bock 51-A-20 Ale 181-B-11 Bock 41-B-15 Bock 21-C-04 Bock 121-C-05 Ale 62-A-02 Ale 242-D-23 Bock 1

Aggregate orders by item

Gets us a nice FIFO picklist picking the total quantities needed by the three orders

But…We can't see how much is for each order?

Page 30: Oracle database - Analytic functions - Advanced cases

with orderbatch as ( select o.item, sum(o.qty) qty from orderline o where o.ordno in (51, 62, 73) group by o.item)

select s.loc, s.item, least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty , sum_prv_qty + 1 from_qty, least(sum_qty, ord_qty) to_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and current row ),0) sum_qty from orderbatch o join inventory i on i.item = o.item ) s where s.sum_prv_qty < s.ord_qty order by s.item, s.purch, s.loc;

Pick quantity intervals

Both rolling sum of previous rows only as well as rolling sum including current row

Calculate from and to quantity of each pick

Page 31: Oracle database - Analytic functions - Advanced cases

LOC ITEM PICK_QTY FROM_QTY TO_QTY------- ----- -------- -------- ------1-A-20 Ale 18 1 182-A-02 Ale 24 19 421-C-05 Ale 6 43 481-B-15 Bock 2 1 21-C-04 Bock 12 3 142-D-23 Bock 1 15 151-B-11 Bock 4 16 191-A-02 Bock 5 20 24

Pick quantity intervals

The 24 Ale picked at 2-A-02 is number 19-42 of the total 48 Ale we are picking

Page 32: Oracle database - Analytic functions - Advanced cases

select o.ordno, o.item, o.qty , nvl(sum(o.qty) over ( partition by o.item order by o.ordno rows between unbounded preceding and 1 preceding ),0) + 1 from_qty , nvl(sum(o.qty) over ( partition by o.item order by o.ordno rows between unbounded preceding and current row ),0) to_qty from orderline o where ordno in (51, 62, 73) order by o.item, o.ordno;

Order quantity intervals

Similarly calculate from and to quantity of the orderlines

Page 33: Oracle database - Analytic functions - Advanced cases

ORDNO ITEM QTY FROM_QTY TO_QTY----- ----- ---- -------- ------ 51 Ale 24 1 24 62 Ale 8 25 32 73 Ale 16 33 48 51 Bock 18 1 18 73 Bock 6 19 24

Order quantity intervals

The 8 Ale from order no 62 is number 25-32 of the total 48 Ale ordered

Page 34: Oracle database - Analytic functions - Advanced cases

with orderlines as ( select o.ordno, o.item, o.qty , nvl(sum(o.qty) over ( partition by o.item order by o.ordno rows between unbounded preceding and 1 preceding ),0) + 1 from_qty , nvl(sum(o.qty) over ( partition by o.item order by o.ordno rows between unbounded preceding and current row ),0) to_qty from orderline o where ordno in (51, 62, 73)), orderbatch as ( select o.item, sum(o.qty) qty from orderlines o group by o.item...

Join on overlapping intervals

Named subquery with the orderlines and their intervals

Named subquery with the aggregate sums by item

>>>

Page 35: Oracle database - Analytic functions - Advanced cases

...

), fifo as ( select s.loc, s.item, s.purch, least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qty , sum_prv_qty + 1 from_qty, least(sum_qty, ord_qty) to_qty from ( select o.item, o.qty ord_qty, i.loc, i.purch, i.qty loc_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and 1 preceding ),0) sum_prv_qty , nvl(sum(i.qty) over ( partition by i.item order by i.purch, i.loc rows between unbounded preceding and current row ),0) sum_qty from orderbatch o join inventory i on i.item = o.item ) s where s.sum_prv_qty < s.ord_qty...

Join on overlapping intervals

Named subquery with FIFO pick of the sums with quantity intervals

>>>

Page 36: Oracle database - Analytic functions - Advanced cases

...

)select f.loc, f.item, f.purch, f.pick_qty, f.from_qty, f.to_qty , o.ordno, o.qty, o.from_qty, o.to_qty from fifo f join orderlines o on o.item = f.item and o.to_qty >= f.from_qty and o.from_qty <= f.to_qty order by f.item, f.purch, o.ordno;

Join on overlapping intervals

Join the fifo subquery with the orderlines subquery on item and overlapping quantity intervals

Page 37: Oracle database - Analytic functions - Advanced cases

LOC ITEM PURCH PICK_QTY FROM_QTY TO_QTY ORDNO QTY FROM_QTY TO_QTY------- ----- ---------- -------- -------- ------ ----- ---- -------- ------1-A-20 Ale 2014-02-01 18 1 18 51 24 1 242-A-02 Ale 2014-02-02 24 19 42 51 24 1 242-A-02 Ale 2014-02-02 24 19 42 62 8 25 322-A-02 Ale 2014-02-02 24 19 42 73 16 33 481-C-05 Ale 2014-02-03 6 43 48 73 16 33 481-B-15 Bock 2014-02-02 2 1 2 51 18 1 181-C-04 Bock 2014-02-03 12 3 14 51 18 1 182-D-23 Bock 2014-02-04 1 15 15 51 18 1 181-B-11 Bock 2014-02-05 4 16 19 51 18 1 181-B-11 Bock 2014-02-05 4 16 19 73 6 19 241-A-02 Bock 2014-02-06 5 20 24 73 6 19 24

Join on overlapping intervals

At location 2-A-02 we pick number 19-42 out of 48 AleThat overlaps with all three orders, as they get respectively number 1-24, 25-32 and 33-48 of the 48 Ale

Page 38: Oracle database - Analytic functions - Advanced cases

with orderlines as (...

), orderbatch as (...

), fifo as (...

)select f.loc, f.item, f.purch, f.pick_qty, f.from_qty, f.to_qty , o.ordno, o.qty, o.from_qty, o.to_qty , least( f.loc_qty , least(o.to_qty, f.to_qty) - greatest(o.from_qty, f.from_qty) + 1 ) pick_ord_qty from fifo f join orderlines o on o.item = f.item and o.to_qty >= f.from_qty and o.from_qty <= f.to_qty order by f.item, f.purch, o.ordno;

How much to pick

Each row gets either the "size of the overlap" or the quantity on the location, whichever is smallest

Page 39: Oracle database - Analytic functions - Advanced cases

LOC ITEM PURCH PICK_QTY FROM_QTY TO_QTY ORDNO QTY FROM_QTY TO_QTY PICK_ORD_QTY------- ----- ---------- -------- -------- ------ ----- ---- -------- ------ ------------1-A-20 Ale 2014-02-01 18 1 18 51 24 1 24 182-A-02 Ale 2014-02-02 24 19 42 51 24 1 24 62-A-02 Ale 2014-02-02 24 19 42 62 8 25 32 82-A-02 Ale 2014-02-02 24 19 42 73 16 33 48 101-C-05 Ale 2014-02-03 6 43 48 73 16 33 48 61-B-15 Bock 2014-02-02 2 1 2 51 18 1 18 21-C-04 Bock 2014-02-03 12 3 14 51 18 1 18 122-D-23 Bock 2014-02-04 1 15 15 51 18 1 18 11-B-11 Bock 2014-02-05 4 16 19 51 18 1 18 31-B-11 Bock 2014-02-05 4 16 19 73 6 19 24 11-A-02 Bock 2014-02-06 5 20 24 73 6 19 24 5

How much to pick

At 2-A-02 we pick 6 Ale to order 51, 8 Ale to order 62 and 10 Ale to order 73 – total 24 Ale from that location

Page 40: Oracle database - Analytic functions - Advanced cases

Batch picklist – FIFOwith orderlines as (...

), orderbatch as (...

), fifo as (...

)select f.loc, f.item , f.pick_qty pick_at_loc, o.ordno , least( f.loc_qty , least(o.to_qty, f.to_qty) - greatest(o.from_qty, f.from_qty) +

1 ) qty_for_ord from fifo f join orderlines o on o.item = f.item and o.to_qty >= f.from_qty and o.from_qty <= f.to_qty order by f.loc, o.ordno;

LOC ITEM PICK_AT_LOC ORDNO QTY_FOR_ORD

------- ----- ----------- ----- -----------

1-A-02 Bock 5 73 5

1-A-20 Ale 18 51 18

1-B-11 Bock 4 51 3

1-B-11 Bock 4 73 1

1-B-15 Bock 2 51 2

1-C-04 Bock 12 51 12

1-C-05 Ale 6 73 6

2-A-02 Ale 24 51 6

2-A-02 Ale 24 62 8

2-A-02 Ale 24 73 10

2-D-23 Bock 1 51 1

Clean up query and keep what's needed for picking operator

Page 41: Oracle database - Analytic functions - Advanced cases

with orderlines as (...), orderbatch as (...), fifo as (...

), pick as ( select to_number(substr(f.loc,1,1)) warehouse , substr(f.loc,3,1) aisle , dense_rank() over ( order by to_number(substr(f.loc,1,1)) -- warehouse , substr(f.loc,3,1) -- aisle ) aisle_no , to_number(substr(f.loc,5,2)) position , f.loc, f.item, f.pick_qty pick_at_loc, o.ordno , least( f.loc_qty , least(o.to_qty, f.to_qty) - greatest(o.from_qty, f.from_qty) + 1 ) qty_for_ord from fifo f join orderlines o on o.item = f.item and o.to_qty >= f.from_qty and o.from_qty <= f.to_qty...

Batch picklist FIFO with picking routeNamed subquery with batch picklist adding warehouse, aisle, position and dense rank

>>>

Page 42: Oracle database - Analytic functions - Advanced cases

Batch picklist FIFO with picking route...

)select p.loc, p.item, p.pick_at_loc , p.ordno, p.qty_for_ord from pick p order by p.warehouse , p.aisle_no , case when mod(p.aisle_no,2) = 1 then p.position else -p.position end;

LOC ITEM PICK_AT_LOC ORDNO QTY_FOR_ORD

------- ----- ----------- ----- -----------

1-A-02 Bock 5 73 5

1-A-20 Ale 18 51 18

1-B-15 Bock 2 51 2

1-B-11 Bock 4 51 3

1-B-11 Bock 4 73 1

1-C-04 Bock 12 51 12

1-C-05 Ale 6 73 6

2-A-02 Ale 24 51 6

2-A-02 Ale 24 73 10

2-A-02 Ale 24 62 8

2-D-23 Bock 1 51 1

Select from the pick subqueryOrdering with odd / even logic

Batch picklist by FIFO with picking route in single SQL finished

Page 43: Oracle database - Analytic functions - Advanced cases

FIFO principle● Or other principles by changing one line of code

Picking route● Up and down alternate aisles

Batch picking● Multiple orders simultaneously

Single SQL picking Done

Case closed

Page 44: Oracle database - Analytic functions - Advanced cases

Picking by FIFO

Sales forecasting

Cases

Page 45: Oracle database - Analytic functions - Advanced cases

Sales forecasting● Seasonal items (summer / winter)● Trending upwards or downwards over time

Regression● Simple model ”transposing graph”● Datascientists model ”Time Series Analysis”

Single SQL● Utilize the power of the database

Case: Sales forecasting

Page 46: Oracle database - Analytic functions - Advanced cases

create table sales ( item varchar2(10) , mth date , qty number);

insert into sales values ('Snowchain', date '2011-01-01', 79);insert into sales values ('Snowchain', date '2011-02-01', 133);insert into sales values ('Snowchain', date '2011-03-01', 24);...

insert into sales values ('Snowchain', date '2013-10-01', 1);insert into sales values ('Snowchain', date '2013-11-01', 73);insert into sales values ('Snowchain', date '2013-12-01', 160);insert into sales values ('Sunshade' , date '2011-01-01', 4);insert into sales values ('Sunshade' , date '2011-02-01', 6);insert into sales values ('Sunshade' , date '2011-03-01', 32);...

insert into sales values ('Sunshade' , date '2013-10-01', 11);insert into sales values ('Sunshade' , date '2013-11-01', 3);insert into sales values ('Sunshade' , date '2013-12-01', 5);

Sales 2011 – 2013

Monthly sales2011 - 2013

Snowchain and Sunshade

Page 47: Oracle database - Analytic functions - Advanced cases

Snowchain peaks wintertime and trends upwardsSunshade peaks summertime and trends downwards

Page 48: Oracle database - Analytic functions - Advanced cases

select sales.item, sales.mth, sales.qty , regr_slope( sales.qty , extract(year from sales.mth) * 12 + extract(month from sales.mth) ) over ( partition by sales.item order by sales.mth range between interval '23' month preceding and current row ) slope from sales order by sales.item, sales.mth;

Moving slope

Calculate slope of linear regression of a graph with qty on the Y axis and month as number with unit 1=month on the X axis"Rolling" slope of 24 points on the graph (= 2 years)

Page 49: Oracle database - Analytic functions - Advanced cases

Moving slopeITEM MTH QTY SLOPE---------- ------- ---- -------Snowchain 2011-01 79Snowchain 2011-02 133 54.000Snowchain 2011-03 24 -27.500Snowchain 2011-04 1 -34.300Snowchain 2011-05 0 -29.000Snowchain 2011-06 0 -23.343...

Snowchain 2013-01 167 1.776Snowchain 2013-02 247 4.821Snowchain 2013-03 42 4.533Snowchain 2013-04 0 3.558Snowchain 2013-05 0 2.574Snowchain 2013-06 0 1.590Snowchain 2013-07 0 .605Snowchain 2013-08 1 -.369Snowchain 2013-09 0 -1.343Snowchain 2013-10 1 -2.274Snowchain 2013-11 73 -2.363Snowchain 2013-12 160 -.991

ITEM MTH QTY SLOPE---------- ------- ---- -------Sunshade 2011-01 4Sunshade 2011-02 6 2.000Sunshade 2011-03 32 14.000Sunshade 2011-04 45 14.900Sunshade 2011-05 62 15.500Sunshade 2011-06 58 12.886...

Sunshade 2013-01 2 -1.135Sunshade 2013-02 8 -1.595Sunshade 2013-03 28 -1.574Sunshade 2013-04 26 -1.428Sunshade 2013-05 23 -1.111Sunshade 2013-06 46 -.574Sunshade 2013-07 73 .537Sunshade 2013-08 25 .560Sunshade 2013-09 13 .421Sunshade 2013-10 11 .217Sunshade 2013-11 3 -.200Sunshade 2013-12 5 -.574

Slopes in 2011 not very useful

Slopes in 2013 based on 2 years data

Page 50: Oracle database - Analytic functions - Advanced cases

Slope of each month is not the same

Page 51: Oracle database - Analytic functions - Advanced cases

select item, mth, qty , qty + 12 * slope qty_next_year from ( select sales.item, sales.mth, sales.qty , regr_slope( sales.qty , extract(year from sales.mth) * 12 + extract(month from sales.mth) ) over ( partition by sales.item order by sales.mth range between interval '23' month preceding and current row ) slope from sales ) where mth >= date '2013-01-01' order by item, mth;

Transpose 12 months

Filter 2013 with the useful slopes

Slope is Y-increment per month12 * Slope is Y-increment per yearAdd 12 * Slope to Qty is forecast

Page 52: Oracle database - Analytic functions - Advanced cases

Transpose 12 monthsITEM MTH QTY QTY_NEXT_YEAR---------- ------- ---- -------------Snowchain 2013-01 167 188.3130Snowchain 2013-02 247 304.8557Snowchain 2013-03 42 96.3913Snowchain 2013-04 0 42.6991Snowchain 2013-05 0 30.8870Snowchain 2013-06 0 19.0748Snowchain 2013-07 0 7.2626Snowchain 2013-08 1 -3.4296Snowchain 2013-09 0 -16.1217Snowchain 2013-10 1 -26.2922Snowchain 2013-11 73 44.6435Snowchain 2013-12 160 148.1096

ITEM MTH QTY QTY_NEXT_YEAR---------- ------- ---- -------------Sunshade 2013-01 2 -11.6174Sunshade 2013-02 8 -11.1374Sunshade 2013-03 28 9.1130Sunshade 2013-04 26 8.8609Sunshade 2013-05 23 9.6643Sunshade 2013-06 46 39.1130Sunshade 2013-07 73 79.4487Sunshade 2013-08 25 31.7148Sunshade 2013-09 13 18.0504Sunshade 2013-10 11 13.6087Sunshade 2013-11 3 .5948Sunshade 2013-12 5 -1.8870

Each month is transposed 12 months into the future by the slope of the previous 24 months

Page 53: Oracle database - Analytic functions - Advanced cases

select item , add_months(mth, 12) mth , greatest(round(qty + 12 * slope), 0) forecast from ( select sales.item, sales.mth, sales.qty , regr_slope( sales.qty , extract(year from sales.mth) * 12 + extract(month from sales.mth) ) over ( partition by sales.item order by sales.mth range between interval '23' month preceding and current row ) slope from sales ) where mth >= date '2013-01-01' order by item, mth;

Rounded forecast

Rather than "qty_next_year", add 12 months to show the month of the forecastRound off to whole quantities and assume negative forecast is a zero sale

Page 54: Oracle database - Analytic functions - Advanced cases

Rounded forecastITEM MTH FORECAST---------- ------- --------Snowchain 2014-01 188Snowchain 2014-02 305Snowchain 2014-03 96Snowchain 2014-04 43Snowchain 2014-05 31Snowchain 2014-06 19Snowchain 2014-07 7Snowchain 2014-08 0Snowchain 2014-09 0Snowchain 2014-10 0Snowchain 2014-11 45Snowchain 2014-12 148

ITEM MTH FORECAST---------- ------- --------Sunshade 2014-01 0Sunshade 2014-02 0Sunshade 2014-03 9Sunshade 2014-04 9Sunshade 2014-05 10Sunshade 2014-06 39Sunshade 2014-07 79Sunshade 2014-08 32Sunshade 2014-09 18Sunshade 2014-10 14Sunshade 2014-11 1Sunshade 2014-12 0

Simple forecast with nice numbers

Page 55: Oracle database - Analytic functions - Advanced cases

select item, mth, qty, type , sum(qty) over (partition by item, extract(year from mth)) qty_yr from ( select sales.item, sales.mth, sales.qty, 'Actual' type from sales union all select item, add_months(mth, 12) mth , greatest(round(qty + 12 * slope), 0) qty, 'Forecast' type from ( select sales.item, sales.mth, sales.qty , regr_slope( sales.qty , extract(year from sales.mth) * 12 + extract(month from sales.mth) ) over ( partition by sales.item order by sales.mth range between interval '23' month preceding and current row ) slope from sales ) where mth >= date '2013-01-01' ) order by item, mth;

Sales and forecast

Union actual sales with forecast

Show year totals for comparison

Page 56: Oracle database - Analytic functions - Advanced cases

Sales and forecastITEM MTH QTY TYPE QTY_YR---------- ------- ---- -------- ------Snowchain 2011-01 79 Actual 331...

Snowchain 2011-12 74 Actual 331Snowchain 2012-01 148 Actual 582...

Snowchain 2012-12 172 Actual 582Snowchain 2013-01 167 Actual 691...

Snowchain 2013-12 160 Actual 691Snowchain 2014-01 188 Forecast 882Snowchain 2014-02 305 Forecast 882Snowchain 2014-03 96 Forecast 882Snowchain 2014-04 43 Forecast 882Snowchain 2014-05 31 Forecast 882Snowchain 2014-06 19 Forecast 882Snowchain 2014-07 7 Forecast 882Snowchain 2014-08 0 Forecast 882Snowchain 2014-09 0 Forecast 882Snowchain 2014-10 0 Forecast 882Snowchain 2014-11 45 Forecast 882Snowchain 2014-12 148 Forecast 882

ITEM MTH QTY TYPE QTY_YR---------- ------- ---- -------- ------Sunshade 2011-01 4 Actual 377...

Sunshade 2011-12 8 Actual 377Sunshade 2012-01 2 Actual 321...

Sunshade 2012-12 3 Actual 321Sunshade 2013-01 2 Actual 263...

Sunshade 2013-12 5 Actual 263Sunshade 2014-01 0 Forecast 211Sunshade 2014-02 0 Forecast 211Sunshade 2014-03 9 Forecast 211Sunshade 2014-04 9 Forecast 211Sunshade 2014-05 10 Forecast 211Sunshade 2014-06 39 Forecast 211Sunshade 2014-07 79 Forecast 211Sunshade 2014-08 32 Forecast 211Sunshade 2014-09 18 Forecast 211Sunshade 2014-10 14 Forecast 211Sunshade 2014-11 1 Forecast 211Sunshade 2014-12 0 Forecast 211

Year totals easy to see trend Snowchain upward, Sunshade downward

Page 57: Oracle database - Analytic functions - Advanced cases

Forecast follow trend up or down but keep graph shape (seasons)

Page 58: Oracle database - Analytic functions - Advanced cases

Our dataanalyst/scientist did a model in Excel● Centered Moving Average● Seasonality● Deseasonalize● Regression trend● Reseasonalize

http://people.duke.edu/~rnau/411outbd.htm OK, I can do that in a SQL statement…

Time Series Analysis

Page 59: Oracle database - Analytic functions - Advanced cases

select sales.item , mths.ts , mths.mth , extract(year from mths.mth) yr , extract(month from mths.mth) mthno , sales.qty from ( select add_months(date '2011-01-01', level-1) mth , level ts --time serie from dual connect by level <= 48 ) mths left outer join sales partition by (sales.item) on sales.mth = mths.mth order by sales.item, mths.mth;

Time Series

Create 48 month time series for each item – sales 2011-13 and forecast 2014

Partitioned outer join gives rows for 2014 for each item with null qty

Page 60: Oracle database - Analytic functions - Advanced cases

Time SeriesITEM TS MTH YR MTHNO QTY---------- --- ------- ----- ----- ----Snowchain 1 2011-01 2011 1 79Snowchain 2 2011-02 2011 2 133Snowchain 3 2011-03 2011 3 24...

Snowchain 34 2013-10 2013 10 1Snowchain 35 2013-11 2013 11 73Snowchain 36 2013-12 2013 12 160Snowchain 37 2014-01 2014 1Snowchain 38 2014-02 2014 2Snowchain 39 2014-03 2014 3Snowchain 40 2014-04 2014 4Snowchain 41 2014-05 2014 5Snowchain 42 2014-06 2014 6Snowchain 43 2014-07 2014 7Snowchain 44 2014-08 2014 8Snowchain 45 2014-09 2014 9Snowchain 46 2014-10 2014 10Snowchain 47 2014-11 2014 11Snowchain 48 2014-12 2014 12

ITEM TS MTH YR MTHNO QTY---------- --- ------- ----- ----- ----Sunshade 1 2011-01 2011 1 4Sunshade 2 2011-02 2011 2 6Sunshade 3 2011-03 2011 3 32...

Sunshade 34 2013-10 2013 10 11Sunshade 35 2013-11 2013 11 3Sunshade 36 2013-12 2013 12 5Sunshade 37 2014-01 2014 1Sunshade 38 2014-02 2014 2Sunshade 39 2014-03 2014 3Sunshade 40 2014-04 2014 4Sunshade 41 2014-05 2014 5Sunshade 42 2014-06 2014 6Sunshade 43 2014-07 2014 7Sunshade 44 2014-08 2014 8Sunshade 45 2014-09 2014 9Sunshade 46 2014-10 2014 10Sunshade 47 2014-11 2014 11Sunshade 48 2014-12 2014 12

Sales in 2011-13, null qty in 2014

Page 61: Oracle database - Analytic functions - Advanced cases

with s1 as (...

)select s1.* , case when ts between 7 and 30 then (nvl(avg(qty) over ( partition by item order by ts rows between 5 preceding and 6 following ),0) + nvl(avg(qty) over ( partition by item order by ts rows between 6 preceding and 5 following ),0)) / 2 else null end cma -- centered moving average from s1 order by item, ts;

Centered Moving Average

• Rolling average -5 to +6 months• Rolling average -6 to +5 monthsAverage of those two is CMADo this only for those months(ts 7-30) where there is 12 months data

Page 62: Oracle database - Analytic functions - Advanced cases

Centered Moving AverageITEM TS MTH YR MTHNO QTY CMA---------- --- ------- ----- ----- ---- -------Snowchain 1 2011-01 2011 1 79Snowchain 2 2011-02 2011 2 133Snowchain 3 2011-03 2011 3 24Snowchain 4 2011-04 2011 4 1Snowchain 5 2011-05 2011 5 0Snowchain 6 2011-06 2011 6 0Snowchain 7 2011-07 2011 7 0 30.458Snowchain 8 2011-08 2011 8 0 36.500Snowchain 9 2011-09 2011 9 1 39.917Snowchain 10 2011-10 2011 10 4 40.208Snowchain 11 2011-11 2011 11 15 40.250Snowchain 12 2011-12 2011 12 74 40.250Snowchain 13 2012-01 2012 1 148 40.250Snowchain 14 2012-02 2012 2 209 40.292Snowchain 15 2012-03 2012 3 30 40.292...

Snowchain 29 2013-05 2013 5 0 56.250Snowchain 30 2013-06 2013 6 0 58.083Snowchain 31 2013-07 2013 7 0Snowchain 32 2013-08 2013 8 1Snowchain 33 2013-09 2013 9 0...

ITEM TS MTH YR MTHNO QTY CMA---------- --- ------- ----- ----- ---- -------Sunshade 1 2011-01 2011 1 4Sunshade 2 2011-02 2011 2 6Sunshade 3 2011-03 2011 3 32Sunshade 4 2011-04 2011 4 45Sunshade 5 2011-05 2011 5 62Sunshade 6 2011-06 2011 6 58Sunshade 7 2011-07 2011 7 85 31.333Sunshade 8 2011-08 2011 8 28 31.542Sunshade 9 2011-09 2011 9 24 31.708Sunshade 10 2011-10 2011 10 19 32.208Sunshade 11 2011-11 2011 11 6 31.458Sunshade 12 2011-12 2011 12 8 30.917Sunshade 13 2012-01 2012 1 2 30.542Sunshade 14 2012-02 2012 2 13 29.083Sunshade 15 2012-03 2012 3 29 28.292...

Sunshade 29 2013-05 2013 5 23 21.833Sunshade 30 2013-06 2013 6 46 21.833Sunshade 31 2013-07 2013 7 73Sunshade 32 2013-08 2013 8 25Sunshade 33 2013-09 2013 9 13...

CMA for 24 months 2011-07 to 2013-06

Page 63: Oracle database - Analytic functions - Advanced cases

with s1 as (...

), s2 as (...

)select s2.* , nvl(avg( case qty when 0 then 0.0001 else qty end / nullif(cma,0) ) over ( partition by item, mthno ),0) s -- seasonality from s2 order by item, ts;

Seasonality factor

Qty divided by CMA factor

Average factor of the month is seasonalityPartition makes seasonality same for all january, all february, etc. (by item)

Page 64: Oracle database - Analytic functions - Advanced cases

Seasonality factorITEM TS MTH YR MTHNO QTY CMA S---------- --- ------- ----- ----- ---- ------- -------...

Snowchain 9 2011-09 2011 9 1 39.917 .0125Snowchain 10 2011-10 2011 10 4 40.208 .0774Snowchain 11 2011-11 2011 11 15 40.250 .3435Snowchain 12 2011-12 2011 12 74 40.250 2.5094Snowchain 13 2012-01 2012 1 148 40.250 3.3824Snowchain 14 2012-02 2012 2 209 40.292 4.8771Snowchain 15 2012-03 2012 3 30 40.292 .7606Snowchain 16 2012-04 2012 4 2 40.208 .0249Snowchain 17 2012-05 2012 5 0 40.250 .0000Snowchain 18 2012-06 2012 6 0 44.417 .0000Snowchain 19 2012-07 2012 7 0 49.292 .0000Snowchain 20 2012-08 2012 8 1 51.667 .0097Snowchain 21 2012-09 2012 9 0 53.750 .0125Snowchain 22 2012-10 2012 10 3 54.167 .0774Snowchain 23 2012-11 2012 11 17 54.083 .3435Snowchain 24 2012-12 2012 12 172 54.083 2.5094Snowchain 25 2013-01 2013 1 167 54.083 3.3824Snowchain 26 2013-02 2013 2 247 54.083 4.8771Snowchain 27 2013-03 2013 3 42 54.083 .7606Snowchain 28 2013-04 2013 4 0 54.000 .0249...

ITEM TS MTH YR MTHNO QTY CMA S---------- --- ------- ----- ----- ---- ------- -------...

Sunshade 9 2011-09 2011 9 24 31.708 .5876Sunshade 10 2011-10 2011 10 19 32.208 .5567Sunshade 11 2011-11 2011 11 6 31.458 .2033Sunshade 12 2011-12 2011 12 8 30.917 .1989Sunshade 13 2012-01 2012 1 2 30.542 .0805Sunshade 14 2012-02 2012 2 13 29.083 .4071Sunshade 15 2012-03 2012 3 29 28.292 1.1489Sunshade 16 2012-04 2012 4 60 27.500 1.6818Sunshade 17 2012-05 2012 5 29 27.208 1.0596Sunshade 18 2012-06 2012 6 78 26.958 2.5001Sunshade 19 2012-07 2012 7 56 26.750 2.4031Sunshade 20 2012-08 2012 8 22 26.542 .8583Sunshade 21 2012-09 2012 9 11 26.292 .5876Sunshade 22 2012-10 2012 10 13 24.833 .5567Sunshade 23 2012-11 2012 11 5 23.167 .2033Sunshade 24 2012-12 2012 12 3 21.583 .1989Sunshade 25 2013-01 2013 1 2 20.958 .0805Sunshade 26 2013-02 2013 2 8 21.792 .4071Sunshade 27 2013-03 2013 3 28 22.000 1.1489Sunshade 28 2013-04 2013 4 26 22.000 1.6818...

S calculated for each monthPartition makes it repeat

Page 65: Oracle database - Analytic functions - Advanced cases

with s1 as (...

), s2 as (...

), s3 as (...

)select s3.* , case when ts <= 36 then nvl(case qty when 0 then 0.0001 else qty end / nullif(s,0), 0) end des -- deseasonalized from s3 order by item, ts;

Deseasonalized quantity

Divide each individual qty by the seasonality factor

Page 66: Oracle database - Analytic functions - Advanced cases

Deseasonalized quantityITEM TS MTH QTY CMA S DES---------- --- ------- ---- ------- ------- --------Snowchain 1 2011-01 79 3.3824 23.356Snowchain 2 2011-02 133 4.8771 27.270Snowchain 3 2011-03 24 .7606 31.555...

Snowchain 22 2012-10 3 54.167 .0774 38.743Snowchain 23 2012-11 17 54.083 .3435 49.490Snowchain 24 2012-12 172 54.083 2.5094 68.542Snowchain 25 2013-01 167 54.083 3.3824 49.373Snowchain 26 2013-02 247 54.083 4.8771 50.645Snowchain 27 2013-03 42 54.083 .7606 55.221Snowchain 28 2013-04 0 54.000 .0249 .004Snowchain 29 2013-05 0 56.250 .0000 46.924Snowchain 30 2013-06 0 58.083 .0000 50.339Snowchain 31 2013-07 0 .0000 37.651Snowchain 32 2013-08 1 .0097 103.319Snowchain 33 2013-09 0 .0125 .008Snowchain 34 2013-10 1 .0774 12.914Snowchain 35 2013-11 73 .3435 212.518Snowchain 36 2013-12 160 2.5094 63.760...

ITEM TS MTH QTY CMA S DES---------- --- ------- ---- ------- ------- --------Sunshade 1 2011-01 4 .0805 49.717Sunshade 2 2011-02 6 .4071 14.740Sunshade 3 2011-03 32 1.1489 27.853...

Sunshade 22 2012-10 13 24.833 .5567 23.352Sunshade 23 2012-11 5 23.167 .2033 24.597Sunshade 24 2012-12 3 21.583 .1989 15.085Sunshade 25 2013-01 2 20.958 .0805 24.858Sunshade 26 2013-02 8 21.792 .4071 19.654Sunshade 27 2013-03 28 22.000 1.1489 24.372Sunshade 28 2013-04 26 22.000 1.6818 15.459Sunshade 29 2013-05 23 21.833 1.0596 21.705Sunshade 30 2013-06 46 21.833 2.5001 18.399Sunshade 31 2013-07 73 2.4031 30.377Sunshade 32 2013-08 25 .8583 29.127Sunshade 33 2013-09 13 .5876 22.122Sunshade 34 2013-10 11 .5567 19.759Sunshade 35 2013-11 3 .2033 14.758Sunshade 36 2013-12 5 .1989 25.141...

DES show trend more clear without seasonal up/down

Page 67: Oracle database - Analytic functions - Advanced cases

Snowchain deseasonalized

Page 68: Oracle database - Analytic functions - Advanced cases

with s1 as (...

), s2 as (...

), s3 as (...

), s4 as (...

)select s4.* , regr_intercept(des,ts) over (partition by item) + ts*regr_slope(des,ts) over (partition by item) t -- trend from s4 order by item, ts;

Trend (regression)

Linear regression of deseasonalized qty gives the trend line

Intercept is the point where the line intersects Y axisAdd slope*time series (month) and get Y value of trend line

Page 69: Oracle database - Analytic functions - Advanced cases

Trend (regression)ITEM TS MTH QTY CMA S DES T---------- --- ------- ---- ------- ------- -------- -------Snowchain 1 2011-01 79 3.3824 23.356 32.163Snowchain 2 2011-02 133 4.8771 27.270 33.096Snowchain 3 2011-03 24 .7606 31.555 34.030...Snowchain 34 2013-10 1 .0774 12.914 62.976Snowchain 35 2013-11 73 .3435 212.518 63.910Snowchain 36 2013-12 160 2.5094 63.760 64.844Snowchain 37 2014-01 3.3824 65.777Snowchain 38 2014-02 4.8771 66.711Snowchain 39 2014-03 .7606 67.645Snowchain 40 2014-04 .0249 68.579Snowchain 41 2014-05 .0000 69.512Snowchain 42 2014-06 .0000 70.446Snowchain 43 2014-07 .0000 71.380Snowchain 44 2014-08 .0097 72.314Snowchain 45 2014-09 .0125 73.247Snowchain 46 2014-10 .0774 74.181Snowchain 47 2014-11 .3435 75.115Snowchain 48 2014-12 2.5094 76.049

ITEM TS MTH QTY CMA S DES T---------- --- ------- ---- ------- ------- -------- -------Sunshade 1 2011-01 4 .0805 49.717 35.860Sunshade 2 2011-02 6 .4071 14.740 35.376Sunshade 3 2011-03 32 1.1489 27.853 34.892...Sunshade 34 2013-10 11 .5567 19.759 19.895Sunshade 35 2013-11 3 .2033 14.758 19.412Sunshade 36 2013-12 5 .1989 25.141 18.928Sunshade 37 2014-01 .0805 18.444Sunshade 38 2014-02 .4071 17.960Sunshade 39 2014-03 1.1489 17.477Sunshade 40 2014-04 1.6818 16.993Sunshade 41 2014-05 1.0596 16.509Sunshade 42 2014-06 2.5001 16.025Sunshade 43 2014-07 2.4031 15.542Sunshade 44 2014-08 .8583 15.058Sunshade 45 2014-09 .5876 14.574Sunshade 46 2014-10 .5567 14.090Sunshade 47 2014-11 .2033 13.606Sunshade 48 2014-12 .1989 13.123

Linear trend line from 2011 to 2014

Page 70: Oracle database - Analytic functions - Advanced cases

Snowchain trend line

Page 71: Oracle database - Analytic functions - Advanced cases

with s1 as (...), s2 as (...), s3 as (...), s4 as (...), s5 as (...)

select s5.* , t * s forecast --reseasonalized from s5 order by item, ts;

Reseasonalize (forecast)

Multiply trend line by seasonality factor and get the qty forecasted by the model

Page 72: Oracle database - Analytic functions - Advanced cases

Reseasonalize (forecast)ITEM TS MTH QTY S DES T FORECAST---------- --- ------- ---- ------- -------- ------- --------Snowchain 1 2011-01 79 3.3824 23.356 32.163 108.788Snowchain 2 2011-02 133 4.8771 27.270 33.096 161.414Snowchain 3 2011-03 24 .7606 31.555 34.030 25.882Snowchain 4 2011-04 1 .0249 40.207 34.964 .870...Snowchain 33 2013-09 0 .0125 .008 62.042 .777Snowchain 34 2013-10 1 .0774 12.914 62.976 4.876Snowchain 35 2013-11 73 .3435 212.518 63.910 21.953Snowchain 36 2013-12 160 2.5094 63.760 64.844 162.718Snowchain 37 2014-01 3.3824 65.777 222.487Snowchain 38 2014-02 4.8771 66.711 325.357Snowchain 39 2014-03 .7606 67.645 51.449Snowchain 40 2014-04 .0249 68.579 1.706Snowchain 41 2014-05 .0000 69.512 .000Snowchain 42 2014-06 .0000 70.446 .000Snowchain 43 2014-07 .0000 71.380 .000Snowchain 44 2014-08 .0097 72.314 .700Snowchain 45 2014-09 .0125 73.247 .918Snowchain 46 2014-10 .0774 74.181 5.744Snowchain 47 2014-11 .3435 75.115 25.802Snowchain 48 2014-12 2.5094 76.049 190.836

ITEM TS MTH QTY S DES T FORECAST---------- --- ------- ---- ------- -------- ------- --------Sunshade 1 2011-01 4 .0805 49.717 35.860 2.885Sunshade 2 2011-02 6 .4071 14.740 35.376 14.400Sunshade 3 2011-03 32 1.1489 27.853 34.892 40.087Sunshade 4 2011-04 45 1.6818 26.757 34.409 57.869...Sunshade 33 2013-09 13 .5876 22.122 20.379 11.976Sunshade 34 2013-10 11 .5567 19.759 19.895 11.076Sunshade 35 2013-11 3 .2033 14.758 19.412 3.946Sunshade 36 2013-12 5 .1989 25.141 18.928 3.764Sunshade 37 2014-01 .0805 18.444 1.484Sunshade 38 2014-02 .4071 17.960 7.311Sunshade 39 2014-03 1.1489 17.477 20.079Sunshade 40 2014-04 1.6818 16.993 28.579Sunshade 41 2014-05 1.0596 16.509 17.494Sunshade 42 2014-06 2.5001 16.025 40.065Sunshade 43 2014-07 2.4031 15.542 37.348Sunshade 44 2014-08 .8583 15.058 12.924Sunshade 45 2014-09 .5876 14.574 8.564Sunshade 46 2014-10 .5567 14.090 7.844Sunshade 47 2014-11 .2033 13.606 2.766Sunshade 48 2014-12 .1989 13.123 2.610

Trend line linearApply seasonality = forecast

Page 73: Oracle database - Analytic functions - Advanced cases

Snowchain reseasonalized forecastCompare to actual data 2011-13

Page 74: Oracle database - Analytic functions - Advanced cases

Sunshade reseasonalized forecastCompare to actual data 2011-13

Page 75: Oracle database - Analytic functions - Advanced cases

with s1 as (...), s2 as (...), s3 as (...), s4 as (...), s5 as (...)

select item , mth , qty , t * s forecast --reseasonalized , sum(qty) over (partition by item, yr) qty_yr , sum(t * s) over (partition by item, yr) fc_yr from s5 order by item, ts;

Model describes reality?

2011-13 qty and forecast can be compared to see how well model described reality

Page 76: Oracle database - Analytic functions - Advanced cases

ITEM MTH QTY FORECAST QTY_YR FC_YR---------- ------- ---- -------- ------ -------Snowchain 2011-01 79 108.788 331 421.70...

Snowchain 2012-01 148 146.687 582 556.14...

Snowchain 2013-01 167 184.587 691 690.57Snowchain 2013-02 247 270.710 691 690.57Snowchain 2013-03 42 42.927 691 690.57Snowchain 2013-04 0 1.427 691 690.57...

Snowchain 2013-09 0 .777 691 690.57Snowchain 2013-10 1 4.876 691 690.57Snowchain 2013-11 73 21.953 691 690.57Snowchain 2013-12 160 162.718 691 690.57Snowchain 2014-01 222.487 825.00Snowchain 2014-02 325.357 825.00Snowchain 2014-03 51.449 825.00Snowchain 2014-04 1.706 825.00Snowchain 2014-05 .000 825.00Snowchain 2014-06 .000 825.00Snowchain 2014-07 .000 825.00Snowchain 2014-08 .700 825.00Snowchain 2014-09 .918 825.00Snowchain 2014-10 5.744 825.00Snowchain 2014-11 25.802 825.00Snowchain 2014-12 190.836 825.00

ITEM MTH QTY FORECAST QTY_YR FC_YR---------- ------- ---- -------- ------ -------Sunshade 2011-01 4 2.885 377 390.59...

Sunshade 2012-01 2 2.418 321 322.75...

Sunshade 2013-01 2 1.951 263 254.91...

Sunshade 2013-04 26 38.342 263 254.91Sunshade 2013-05 23 23.645 263 254.91Sunshade 2013-06 46 54.579 263 254.91Sunshade 2013-07 73 51.299 263 254.91Sunshade 2013-08 25 17.907 263 254.91...

Sunshade 2013-11 3 3.946 263 254.91Sunshade 2013-12 5 3.764 263 254.91Sunshade 2014-01 1.484 187.07Sunshade 2014-02 7.311 187.07Sunshade 2014-03 20.079 187.07Sunshade 2014-04 28.579 187.07Sunshade 2014-05 17.494 187.07Sunshade 2014-06 40.065 187.07Sunshade 2014-07 37.348 187.07Sunshade 2014-08 12.924 187.07Sunshade 2014-09 8.564 187.07Sunshade 2014-10 7.844 187.07Sunshade 2014-11 2.766 187.07Sunshade 2014-12 2.610 187.07

Compare numbers and see if model matches reality

Page 77: Oracle database - Analytic functions - Advanced cases

with s1 as (...), s2 as (...), s3 as (...), s4 as (...), s5 as (...)

select item, mth , case when ts <= 36 then qty else round(t * s) end qty , case when ts <= 36 then 'Actual' else 'Forecast' end type , sum( case when ts <= 36 then qty else round(t * s) end ) over ( partition by item, extract(year from mth) ) qty_yr from s5 order by item, ts;

Sales and forecast

Output of Actual and Forecast like we did with simple model

Page 78: Oracle database - Analytic functions - Advanced cases

Sales and forecastITEM MTH QTY TYPE QTY_YR---------- ------- ---- -------- ------Snowchain 2011-01 79 Actual 331...

Snowchain 2011-12 74 Actual 331Snowchain 2012-01 148 Actual 582...

Snowchain 2012-12 172 Actual 582Snowchain 2013-01 167 Actual 691...

Snowchain 2013-12 160 Actual 691Snowchain 2014-01 222 Forecast 825Snowchain 2014-02 325 Forecast 825Snowchain 2014-03 51 Forecast 825Snowchain 2014-04 2 Forecast 825Snowchain 2014-05 0 Forecast 825Snowchain 2014-06 0 Forecast 825Snowchain 2014-07 0 Forecast 825Snowchain 2014-08 1 Forecast 825Snowchain 2014-09 1 Forecast 825Snowchain 2014-10 6 Forecast 825Snowchain 2014-11 26 Forecast 825Snowchain 2014-12 191 Forecast 825

ITEM MTH QTY TYPE QTY_YR---------- ------- ---- -------- ------Sunshade 2011-01 4 Actual 377...

Sunshade 2011-12 8 Actual 377Sunshade 2012-01 2 Actual 321...

Sunshade 2012-12 3 Actual 321Sunshade 2013-01 2 Actual 263...

Sunshade 2013-12 5 Actual 263Sunshade 2014-01 1 Forecast 187Sunshade 2014-02 7 Forecast 187Sunshade 2014-03 20 Forecast 187Sunshade 2014-04 29 Forecast 187Sunshade 2014-05 17 Forecast 187Sunshade 2014-06 40 Forecast 187Sunshade 2014-07 37 Forecast 187Sunshade 2014-08 13 Forecast 187Sunshade 2014-09 9 Forecast 187Sunshade 2014-10 8 Forecast 187Sunshade 2014-11 3 Forecast 187Sunshade 2014-12 3 Forecast 187

This model also follows up/down trends

Page 79: Oracle database - Analytic functions - Advanced cases

Time series slightly more conservative…

Page 80: Oracle database - Analytic functions - Advanced cases

…probably due to negative forecasts in simple model

…than simple transpose model…

Page 81: Oracle database - Analytic functions - Advanced cases

Data analyst develops predictive Time Series Analysis model of reality in Excel

Recreate that model in SQL for repeated application to larger datasets

Query where model fits reality or not Single SQL forecasting Done

Case closed

Page 82: Oracle database - Analytic functions - Advanced cases

Danish geek Oracle SQL Evangelist Oracle PL/SQL Developer Likes to cook Reads sci-fi Member of

Danish Beer Enthusiasts

Questions ?

http://dspsd.blogspot.com

http://dk.linkedin.com/in/kibeha/

@kibeha

Kim Berg Hansen

http://goo.gl/q1YJRL

for this presentation

and scripts