SlideShare a Scribd company logo
Analytic Functions
Advanced Cases
Kim Berg Hansen
T. Hansen Gruppen A/S
Picking by FIFO
 Sales forecasting
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
Warehouses
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');
Inventory
loc = 1-A-20
1 = warehouse
A = aisle
20 = position
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 order
24 Ale
18 Bock
Join up
select 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 18
Ale 24 2-A-02 2014-02-02 24
Ale 24 1-C-05 2014-02-03 18
Ale 24 2-D-07 2014-02-04 9
Ale 24 1-A-31 2014-02-05 12
Bock 18 1-B-15 2014-02-02 2
Bock 18 1-C-04 2014-02-03 12
Bock 18 2-D-23 2014-02-04 1
Bock 18 1-B-11 2014-02-05 4
Bock 18 1-A-02 2014-02-06 18
Order locations for each item by purchase date
Visually easy to see what we need to pick
18 Ale of the oldest and 6 of the next and so on
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 sum
Partition for each item
Order by date
Rolling sum of all
previous rows
ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY
----- ------- ------- ---------- ------- -----------
Ale 24 1-A-20 2014-02-01 18
Ale 24 2-A-02 2014-02-02 24 18
Ale 24 1-C-05 2014-02-03 18 42
Ale 24 2-D-07 2014-02-04 9 60
Ale 24 1-A-31 2014-02-05 12 69
Bock 18 1-B-15 2014-02-02 2
Bock 18 1-C-04 2014-02-03 12 2
Bock 18 2-D-23 2014-02-04 1 14
Bock 18 1-B-11 2014-02-05 4 15
Bock 18 1-A-02 2014-02-06 18 19
Rolling sum of previous
Each row can now evaluate if sufficient has been picked
If the sum of all previous rows is less than the ordered quantity
we still need to pick something and the row is needed
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
ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY PICK_QTY
----- ------- ------- ---------- ------- ----------- --------
Ale 24 1-A-20 2014-02-01 18 0 18
Ale 24 2-A-02 2014-02-02 24 18 6
Bock 18 1-B-15 2014-02-02 2 0 2
Bock 18 1-C-04 2014-02-03 12 2 12
Bock 18 2-D-23 2014-02-04 1 14 1
Bock 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)
Picklist – FIFO
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 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 18
1-B-11 Bock 3
1-B-15 Bock 2
1-C-04 Bock 12
2-A-02 Ale 6
2-D-23 Bock 1
Simple FIFO picklist
Item and quantity to pick
By location order
Picklist – Shortest route
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.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 18
1-A-20 Ale 18
1-A-31 Ale 6
Switch picking strategy =
Switch analytic order by
Keep "outer" order by
Picklist – Least number of picks
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.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 18
2-A-02 Ale 24
Switch picking strategy =
Switch analytic order by
Keep "outer" order by
Picklist – Clean out small quantities
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.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 3
1-A-31 Ale 12
1-B-11 Bock 4
1-B-15 Bock 2
1-C-04 Bock 11
2-D-07 Ale 9
2-D-23 Bock 1
Switch picking strategy =
Switch analytic order by
Keep "outer" order by
Not the greatest picking route
Strategy "Clean out small quantities" give most number of picks
We use that for picking route demonstration
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
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
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
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
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"
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
Much better picking route
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
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 counter
So the first aisle in each warehouse starts by 1
and therefore is odd and positions ordered ascending
Restart count if only one door
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
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
LOC ITEM PICK_QTY
------- ----- --------
1-A-02 Bock 5
1-A-20 Ale 18
1-B-11 Bock 4
1-B-15 Bock 2
1-C-04 Bock 12
1-C-05 Ale 6
2-A-02 Ale 24
2-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?
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
LOC ITEM PICK_QTY FROM_QTY TO_QTY
------- ----- -------- -------- ------
1-A-20 Ale 18 1 18
2-A-02 Ale 24 19 42
1-C-05 Ale 6 43 48
1-B-15 Bock 2 1 2
1-C-04 Bock 12 3 14
2-D-23 Bock 1 15 15
1-B-11 Bock 4 16 19
1-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
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
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
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
>>>
...
), 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
>>>
...
)
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
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 24
2-A-02 Ale 2014-02-02 24 19 42 51 24 1 24
2-A-02 Ale 2014-02-02 24 19 42 62 8 25 32
2-A-02 Ale 2014-02-02 24 19 42 73 16 33 48
1-C-05 Ale 2014-02-03 6 43 48 73 16 33 48
1-B-15 Bock 2014-02-02 2 1 2 51 18 1 18
1-C-04 Bock 2014-02-03 12 3 14 51 18 1 18
2-D-23 Bock 2014-02-04 1 15 15 51 18 1 18
1-B-11 Bock 2014-02-05 4 16 19 51 18 1 18
1-B-11 Bock 2014-02-05 4 16 19 73 6 19 24
1-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 Ale
That overlaps with all three orders, as they get respectively
number 1-24, 25-32 and 33-48 of the 48 Ale
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
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 18
2-A-02 Ale 2014-02-02 24 19 42 51 24 1 24 6
2-A-02 Ale 2014-02-02 24 19 42 62 8 25 32 8
2-A-02 Ale 2014-02-02 24 19 42 73 16 33 48 10
1-C-05 Ale 2014-02-03 6 43 48 73 16 33 48 6
1-B-15 Bock 2014-02-02 2 1 2 51 18 1 18 2
1-C-04 Bock 2014-02-03 12 3 14 51 18 1 18 12
2-D-23 Bock 2014-02-04 1 15 15 51 18 1 18 1
1-B-11 Bock 2014-02-05 4 16 19 51 18 1 18 3
1-B-11 Bock 2014-02-05 4 16 19 73 6 19 24 1
1-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
Batch picklist – FIFO
with 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
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 route
Named subquery with batch picklist adding
warehouse, aisle, position and dense rank
>>>
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 subquery
Ordering with odd / even logic
Batch picklist by FIFO with
picking route in single SQL
finished 
 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
 Picking by FIFO
Sales forecasting
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
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 sales
2011 - 2013
Snowchain and
Sunshade
Snowchain peaks wintertime and trends upwards
Sunshade peaks summertime and trends downwards
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)
Moving slope
ITEM MTH QTY SLOPE
---------- ------- ---- -------
Snowchain 2011-01 79
Snowchain 2011-02 133 54.000
Snowchain 2011-03 24 -27.500
Snowchain 2011-04 1 -34.300
Snowchain 2011-05 0 -29.000
Snowchain 2011-06 0 -23.343
...
Snowchain 2013-01 167 1.776
Snowchain 2013-02 247 4.821
Snowchain 2013-03 42 4.533
Snowchain 2013-04 0 3.558
Snowchain 2013-05 0 2.574
Snowchain 2013-06 0 1.590
Snowchain 2013-07 0 .605
Snowchain 2013-08 1 -.369
Snowchain 2013-09 0 -1.343
Snowchain 2013-10 1 -2.274
Snowchain 2013-11 73 -2.363
Snowchain 2013-12 160 -.991
ITEM MTH QTY SLOPE
---------- ------- ---- -------
Sunshade 2011-01 4
Sunshade 2011-02 6 2.000
Sunshade 2011-03 32 14.000
Sunshade 2011-04 45 14.900
Sunshade 2011-05 62 15.500
Sunshade 2011-06 58 12.886
...
Sunshade 2013-01 2 -1.135
Sunshade 2013-02 8 -1.595
Sunshade 2013-03 28 -1.574
Sunshade 2013-04 26 -1.428
Sunshade 2013-05 23 -1.111
Sunshade 2013-06 46 -.574
Sunshade 2013-07 73 .537
Sunshade 2013-08 25 .560
Sunshade 2013-09 13 .421
Sunshade 2013-10 11 .217
Sunshade 2013-11 3 -.200
Sunshade 2013-12 5 -.574
Slopes in
2011 not
very useful
Slopes in
2013 based
on 2 years
data
Slope of each month is not the same
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 month
12 * Slope is Y-increment per year
Add 12 * Slope to Qty is forecast
Transpose 12 months
ITEM MTH QTY QTY_NEXT_YEAR
---------- ------- ---- -------------
Snowchain 2013-01 167 188.3130
Snowchain 2013-02 247 304.8557
Snowchain 2013-03 42 96.3913
Snowchain 2013-04 0 42.6991
Snowchain 2013-05 0 30.8870
Snowchain 2013-06 0 19.0748
Snowchain 2013-07 0 7.2626
Snowchain 2013-08 1 -3.4296
Snowchain 2013-09 0 -16.1217
Snowchain 2013-10 1 -26.2922
Snowchain 2013-11 73 44.6435
Snowchain 2013-12 160 148.1096
ITEM MTH QTY QTY_NEXT_YEAR
---------- ------- ---- -------------
Sunshade 2013-01 2 -11.6174
Sunshade 2013-02 8 -11.1374
Sunshade 2013-03 28 9.1130
Sunshade 2013-04 26 8.8609
Sunshade 2013-05 23 9.6643
Sunshade 2013-06 46 39.1130
Sunshade 2013-07 73 79.4487
Sunshade 2013-08 25 31.7148
Sunshade 2013-09 13 18.0504
Sunshade 2013-10 11 13.6087
Sunshade 2013-11 3 .5948
Sunshade 2013-12 5 -1.8870
Each month is transposed 12 months into the future
by the slope of the previous 24 months
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 forecast
Round off to whole quantities and assume
negative forecast is a zero sale
Rounded forecast
ITEM MTH FORECAST
---------- ------- --------
Snowchain 2014-01 188
Snowchain 2014-02 305
Snowchain 2014-03 96
Snowchain 2014-04 43
Snowchain 2014-05 31
Snowchain 2014-06 19
Snowchain 2014-07 7
Snowchain 2014-08 0
Snowchain 2014-09 0
Snowchain 2014-10 0
Snowchain 2014-11 45
Snowchain 2014-12 148
ITEM MTH FORECAST
---------- ------- --------
Sunshade 2014-01 0
Sunshade 2014-02 0
Sunshade 2014-03 9
Sunshade 2014-04 9
Sunshade 2014-05 10
Sunshade 2014-06 39
Sunshade 2014-07 79
Sunshade 2014-08 32
Sunshade 2014-09 18
Sunshade 2014-10 14
Sunshade 2014-11 1
Sunshade 2014-12 0
Simple forecast with nice numbers
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
Sales and forecast
ITEM MTH QTY TYPE QTY_YR
---------- ------- ---- -------- ------
Snowchain 2011-01 79 Actual 331
...
Snowchain 2011-12 74 Actual 331
Snowchain 2012-01 148 Actual 582
...
Snowchain 2012-12 172 Actual 582
Snowchain 2013-01 167 Actual 691
...
Snowchain 2013-12 160 Actual 691
Snowchain 2014-01 188 Forecast 882
Snowchain 2014-02 305 Forecast 882
Snowchain 2014-03 96 Forecast 882
Snowchain 2014-04 43 Forecast 882
Snowchain 2014-05 31 Forecast 882
Snowchain 2014-06 19 Forecast 882
Snowchain 2014-07 7 Forecast 882
Snowchain 2014-08 0 Forecast 882
Snowchain 2014-09 0 Forecast 882
Snowchain 2014-10 0 Forecast 882
Snowchain 2014-11 45 Forecast 882
Snowchain 2014-12 148 Forecast 882
ITEM MTH QTY TYPE QTY_YR
---------- ------- ---- -------- ------
Sunshade 2011-01 4 Actual 377
...
Sunshade 2011-12 8 Actual 377
Sunshade 2012-01 2 Actual 321
...
Sunshade 2012-12 3 Actual 321
Sunshade 2013-01 2 Actual 263
...
Sunshade 2013-12 5 Actual 263
Sunshade 2014-01 0 Forecast 211
Sunshade 2014-02 0 Forecast 211
Sunshade 2014-03 9 Forecast 211
Sunshade 2014-04 9 Forecast 211
Sunshade 2014-05 10 Forecast 211
Sunshade 2014-06 39 Forecast 211
Sunshade 2014-07 79 Forecast 211
Sunshade 2014-08 32 Forecast 211
Sunshade 2014-09 18 Forecast 211
Sunshade 2014-10 14 Forecast 211
Sunshade 2014-11 1 Forecast 211
Sunshade 2014-12 0 Forecast 211
Year totals
easy to
see trend
Snowchain
upward,
Sunshade
downward
Forecast follow
trend up or down
but keep graph
shape (seasons)
 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
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
Time Series
ITEM TS MTH YR MTHNO QTY
---------- --- ------- ----- ----- ----
Snowchain 1 2011-01 2011 1 79
Snowchain 2 2011-02 2011 2 133
Snowchain 3 2011-03 2011 3 24
...
Snowchain 34 2013-10 2013 10 1
Snowchain 35 2013-11 2013 11 73
Snowchain 36 2013-12 2013 12 160
Snowchain 37 2014-01 2014 1
Snowchain 38 2014-02 2014 2
Snowchain 39 2014-03 2014 3
Snowchain 40 2014-04 2014 4
Snowchain 41 2014-05 2014 5
Snowchain 42 2014-06 2014 6
Snowchain 43 2014-07 2014 7
Snowchain 44 2014-08 2014 8
Snowchain 45 2014-09 2014 9
Snowchain 46 2014-10 2014 10
Snowchain 47 2014-11 2014 11
Snowchain 48 2014-12 2014 12
ITEM TS MTH YR MTHNO QTY
---------- --- ------- ----- ----- ----
Sunshade 1 2011-01 2011 1 4
Sunshade 2 2011-02 2011 2 6
Sunshade 3 2011-03 2011 3 32
...
Sunshade 34 2013-10 2013 10 11
Sunshade 35 2013-11 2013 11 3
Sunshade 36 2013-12 2013 12 5
Sunshade 37 2014-01 2014 1
Sunshade 38 2014-02 2014 2
Sunshade 39 2014-03 2014 3
Sunshade 40 2014-04 2014 4
Sunshade 41 2014-05 2014 5
Sunshade 42 2014-06 2014 6
Sunshade 43 2014-07 2014 7
Sunshade 44 2014-08 2014 8
Sunshade 45 2014-09 2014 9
Sunshade 46 2014-10 2014 10
Sunshade 47 2014-11 2014 11
Sunshade 48 2014-12 2014 12
Sales in 2011-13, null qty in 2014
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 months
Average of those
two is CMA
Do this only for
those months
(ts 7-30) where
there is 12 months
data
Centered Moving Average
ITEM TS MTH YR MTHNO QTY CMA
---------- --- ------- ----- ----- ---- -------
Snowchain 1 2011-01 2011 1 79
Snowchain 2 2011-02 2011 2 133
Snowchain 3 2011-03 2011 3 24
Snowchain 4 2011-04 2011 4 1
Snowchain 5 2011-05 2011 5 0
Snowchain 6 2011-06 2011 6 0
Snowchain 7 2011-07 2011 7 0 30.458
Snowchain 8 2011-08 2011 8 0 36.500
Snowchain 9 2011-09 2011 9 1 39.917
Snowchain 10 2011-10 2011 10 4 40.208
Snowchain 11 2011-11 2011 11 15 40.250
Snowchain 12 2011-12 2011 12 74 40.250
Snowchain 13 2012-01 2012 1 148 40.250
Snowchain 14 2012-02 2012 2 209 40.292
Snowchain 15 2012-03 2012 3 30 40.292
...
Snowchain 29 2013-05 2013 5 0 56.250
Snowchain 30 2013-06 2013 6 0 58.083
Snowchain 31 2013-07 2013 7 0
Snowchain 32 2013-08 2013 8 1
Snowchain 33 2013-09 2013 9 0
...
ITEM TS MTH YR MTHNO QTY CMA
---------- --- ------- ----- ----- ---- -------
Sunshade 1 2011-01 2011 1 4
Sunshade 2 2011-02 2011 2 6
Sunshade 3 2011-03 2011 3 32
Sunshade 4 2011-04 2011 4 45
Sunshade 5 2011-05 2011 5 62
Sunshade 6 2011-06 2011 6 58
Sunshade 7 2011-07 2011 7 85 31.333
Sunshade 8 2011-08 2011 8 28 31.542
Sunshade 9 2011-09 2011 9 24 31.708
Sunshade 10 2011-10 2011 10 19 32.208
Sunshade 11 2011-11 2011 11 6 31.458
Sunshade 12 2011-12 2011 12 8 30.917
Sunshade 13 2012-01 2012 1 2 30.542
Sunshade 14 2012-02 2012 2 13 29.083
Sunshade 15 2012-03 2012 3 29 28.292
...
Sunshade 29 2013-05 2013 5 23 21.833
Sunshade 30 2013-06 2013 6 46 21.833
Sunshade 31 2013-07 2013 7 73
Sunshade 32 2013-08 2013 8 25
Sunshade 33 2013-09 2013 9 13
...
CMA for 24 months
2011-07 to 2013-06
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 seasonality
Partition makes seasonality
same for all january, all
february, etc. (by item)
Seasonality factor
ITEM TS MTH YR MTHNO QTY CMA S
---------- --- ------- ----- ----- ---- ------- -------
...
Snowchain 9 2011-09 2011 9 1 39.917 .0125
Snowchain 10 2011-10 2011 10 4 40.208 .0774
Snowchain 11 2011-11 2011 11 15 40.250 .3435
Snowchain 12 2011-12 2011 12 74 40.250 2.5094
Snowchain 13 2012-01 2012 1 148 40.250 3.3824
Snowchain 14 2012-02 2012 2 209 40.292 4.8771
Snowchain 15 2012-03 2012 3 30 40.292 .7606
Snowchain 16 2012-04 2012 4 2 40.208 .0249
Snowchain 17 2012-05 2012 5 0 40.250 .0000
Snowchain 18 2012-06 2012 6 0 44.417 .0000
Snowchain 19 2012-07 2012 7 0 49.292 .0000
Snowchain 20 2012-08 2012 8 1 51.667 .0097
Snowchain 21 2012-09 2012 9 0 53.750 .0125
Snowchain 22 2012-10 2012 10 3 54.167 .0774
Snowchain 23 2012-11 2012 11 17 54.083 .3435
Snowchain 24 2012-12 2012 12 172 54.083 2.5094
Snowchain 25 2013-01 2013 1 167 54.083 3.3824
Snowchain 26 2013-02 2013 2 247 54.083 4.8771
Snowchain 27 2013-03 2013 3 42 54.083 .7606
Snowchain 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 .5876
Sunshade 10 2011-10 2011 10 19 32.208 .5567
Sunshade 11 2011-11 2011 11 6 31.458 .2033
Sunshade 12 2011-12 2011 12 8 30.917 .1989
Sunshade 13 2012-01 2012 1 2 30.542 .0805
Sunshade 14 2012-02 2012 2 13 29.083 .4071
Sunshade 15 2012-03 2012 3 29 28.292 1.1489
Sunshade 16 2012-04 2012 4 60 27.500 1.6818
Sunshade 17 2012-05 2012 5 29 27.208 1.0596
Sunshade 18 2012-06 2012 6 78 26.958 2.5001
Sunshade 19 2012-07 2012 7 56 26.750 2.4031
Sunshade 20 2012-08 2012 8 22 26.542 .8583
Sunshade 21 2012-09 2012 9 11 26.292 .5876
Sunshade 22 2012-10 2012 10 13 24.833 .5567
Sunshade 23 2012-11 2012 11 5 23.167 .2033
Sunshade 24 2012-12 2012 12 3 21.583 .1989
Sunshade 25 2013-01 2013 1 2 20.958 .0805
Sunshade 26 2013-02 2013 2 8 21.792 .4071
Sunshade 27 2013-03 2013 3 28 22.000 1.1489
Sunshade 28 2013-04 2013 4 26 22.000 1.6818
...
S calculated for each month
Partition makes it repeat
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
Deseasonalized quantity
ITEM TS MTH QTY CMA S DES
---------- --- ------- ---- ------- ------- --------
Snowchain 1 2011-01 79 3.3824 23.356
Snowchain 2 2011-02 133 4.8771 27.270
Snowchain 3 2011-03 24 .7606 31.555
...
Snowchain 22 2012-10 3 54.167 .0774 38.743
Snowchain 23 2012-11 17 54.083 .3435 49.490
Snowchain 24 2012-12 172 54.083 2.5094 68.542
Snowchain 25 2013-01 167 54.083 3.3824 49.373
Snowchain 26 2013-02 247 54.083 4.8771 50.645
Snowchain 27 2013-03 42 54.083 .7606 55.221
Snowchain 28 2013-04 0 54.000 .0249 .004
Snowchain 29 2013-05 0 56.250 .0000 46.924
Snowchain 30 2013-06 0 58.083 .0000 50.339
Snowchain 31 2013-07 0 .0000 37.651
Snowchain 32 2013-08 1 .0097 103.319
Snowchain 33 2013-09 0 .0125 .008
Snowchain 34 2013-10 1 .0774 12.914
Snowchain 35 2013-11 73 .3435 212.518
Snowchain 36 2013-12 160 2.5094 63.760
...
ITEM TS MTH QTY CMA S DES
---------- --- ------- ---- ------- ------- --------
Sunshade 1 2011-01 4 .0805 49.717
Sunshade 2 2011-02 6 .4071 14.740
Sunshade 3 2011-03 32 1.1489 27.853
...
Sunshade 22 2012-10 13 24.833 .5567 23.352
Sunshade 23 2012-11 5 23.167 .2033 24.597
Sunshade 24 2012-12 3 21.583 .1989 15.085
Sunshade 25 2013-01 2 20.958 .0805 24.858
Sunshade 26 2013-02 8 21.792 .4071 19.654
Sunshade 27 2013-03 28 22.000 1.1489 24.372
Sunshade 28 2013-04 26 22.000 1.6818 15.459
Sunshade 29 2013-05 23 21.833 1.0596 21.705
Sunshade 30 2013-06 46 21.833 2.5001 18.399
Sunshade 31 2013-07 73 2.4031 30.377
Sunshade 32 2013-08 25 .8583 29.127
Sunshade 33 2013-09 13 .5876 22.122
Sunshade 34 2013-10 11 .5567 19.759
Sunshade 35 2013-11 3 .2033 14.758
Sunshade 36 2013-12 5 .1989 25.141
...
DES show trend more clear
without seasonal up/down
Snowchain deseasonalized
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 axis
Add slope*time series (month) and get Y value of trend line
Trend (regression)
ITEM TS MTH QTY CMA S DES T
---------- --- ------- ---- ------- ------- -------- -------
Snowchain 1 2011-01 79 3.3824 23.356 32.163
Snowchain 2 2011-02 133 4.8771 27.270 33.096
Snowchain 3 2011-03 24 .7606 31.555 34.030
...
Snowchain 34 2013-10 1 .0774 12.914 62.976
Snowchain 35 2013-11 73 .3435 212.518 63.910
Snowchain 36 2013-12 160 2.5094 63.760 64.844
Snowchain 37 2014-01 3.3824 65.777
Snowchain 38 2014-02 4.8771 66.711
Snowchain 39 2014-03 .7606 67.645
Snowchain 40 2014-04 .0249 68.579
Snowchain 41 2014-05 .0000 69.512
Snowchain 42 2014-06 .0000 70.446
Snowchain 43 2014-07 .0000 71.380
Snowchain 44 2014-08 .0097 72.314
Snowchain 45 2014-09 .0125 73.247
Snowchain 46 2014-10 .0774 74.181
Snowchain 47 2014-11 .3435 75.115
Snowchain 48 2014-12 2.5094 76.049
ITEM TS MTH QTY CMA S DES T
---------- --- ------- ---- ------- ------- -------- -------
Sunshade 1 2011-01 4 .0805 49.717 35.860
Sunshade 2 2011-02 6 .4071 14.740 35.376
Sunshade 3 2011-03 32 1.1489 27.853 34.892
...
Sunshade 34 2013-10 11 .5567 19.759 19.895
Sunshade 35 2013-11 3 .2033 14.758 19.412
Sunshade 36 2013-12 5 .1989 25.141 18.928
Sunshade 37 2014-01 .0805 18.444
Sunshade 38 2014-02 .4071 17.960
Sunshade 39 2014-03 1.1489 17.477
Sunshade 40 2014-04 1.6818 16.993
Sunshade 41 2014-05 1.0596 16.509
Sunshade 42 2014-06 2.5001 16.025
Sunshade 43 2014-07 2.4031 15.542
Sunshade 44 2014-08 .8583 15.058
Sunshade 45 2014-09 .5876 14.574
Sunshade 46 2014-10 .5567 14.090
Sunshade 47 2014-11 .2033 13.606
Sunshade 48 2014-12 .1989 13.123
Linear trend line from
2011 to 2014
Snowchain trend line
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
Reseasonalize (forecast)
ITEM TS MTH QTY S DES T FORECAST
---------- --- ------- ---- ------- -------- ------- --------
Snowchain 1 2011-01 79 3.3824 23.356 32.163 108.788
Snowchain 2 2011-02 133 4.8771 27.270 33.096 161.414
Snowchain 3 2011-03 24 .7606 31.555 34.030 25.882
Snowchain 4 2011-04 1 .0249 40.207 34.964 .870
...
Snowchain 33 2013-09 0 .0125 .008 62.042 .777
Snowchain 34 2013-10 1 .0774 12.914 62.976 4.876
Snowchain 35 2013-11 73 .3435 212.518 63.910 21.953
Snowchain 36 2013-12 160 2.5094 63.760 64.844 162.718
Snowchain 37 2014-01 3.3824 65.777 222.487
Snowchain 38 2014-02 4.8771 66.711 325.357
Snowchain 39 2014-03 .7606 67.645 51.449
Snowchain 40 2014-04 .0249 68.579 1.706
Snowchain 41 2014-05 .0000 69.512 .000
Snowchain 42 2014-06 .0000 70.446 .000
Snowchain 43 2014-07 .0000 71.380 .000
Snowchain 44 2014-08 .0097 72.314 .700
Snowchain 45 2014-09 .0125 73.247 .918
Snowchain 46 2014-10 .0774 74.181 5.744
Snowchain 47 2014-11 .3435 75.115 25.802
Snowchain 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.885
Sunshade 2 2011-02 6 .4071 14.740 35.376 14.400
Sunshade 3 2011-03 32 1.1489 27.853 34.892 40.087
Sunshade 4 2011-04 45 1.6818 26.757 34.409 57.869
...
Sunshade 33 2013-09 13 .5876 22.122 20.379 11.976
Sunshade 34 2013-10 11 .5567 19.759 19.895 11.076
Sunshade 35 2013-11 3 .2033 14.758 19.412 3.946
Sunshade 36 2013-12 5 .1989 25.141 18.928 3.764
Sunshade 37 2014-01 .0805 18.444 1.484
Sunshade 38 2014-02 .4071 17.960 7.311
Sunshade 39 2014-03 1.1489 17.477 20.079
Sunshade 40 2014-04 1.6818 16.993 28.579
Sunshade 41 2014-05 1.0596 16.509 17.494
Sunshade 42 2014-06 2.5001 16.025 40.065
Sunshade 43 2014-07 2.4031 15.542 37.348
Sunshade 44 2014-08 .8583 15.058 12.924
Sunshade 45 2014-09 .5876 14.574 8.564
Sunshade 46 2014-10 .5567 14.090 7.844
Sunshade 47 2014-11 .2033 13.606 2.766
Sunshade 48 2014-12 .1989 13.123 2.610
Trend line linear
Apply seasonality
= forecast
Snowchain reseasonalized forecast
Compare to actual data 2011-13
Sunshade reseasonalized forecast
Compare to actual data 2011-13
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
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.57
Snowchain 2013-02 247 270.710 691 690.57
Snowchain 2013-03 42 42.927 691 690.57
Snowchain 2013-04 0 1.427 691 690.57
...
Snowchain 2013-09 0 .777 691 690.57
Snowchain 2013-10 1 4.876 691 690.57
Snowchain 2013-11 73 21.953 691 690.57
Snowchain 2013-12 160 162.718 691 690.57
Snowchain 2014-01 222.487 825.00
Snowchain 2014-02 325.357 825.00
Snowchain 2014-03 51.449 825.00
Snowchain 2014-04 1.706 825.00
Snowchain 2014-05 .000 825.00
Snowchain 2014-06 .000 825.00
Snowchain 2014-07 .000 825.00
Snowchain 2014-08 .700 825.00
Snowchain 2014-09 .918 825.00
Snowchain 2014-10 5.744 825.00
Snowchain 2014-11 25.802 825.00
Snowchain 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.91
Sunshade 2013-05 23 23.645 263 254.91
Sunshade 2013-06 46 54.579 263 254.91
Sunshade 2013-07 73 51.299 263 254.91
Sunshade 2013-08 25 17.907 263 254.91
...
Sunshade 2013-11 3 3.946 263 254.91
Sunshade 2013-12 5 3.764 263 254.91
Sunshade 2014-01 1.484 187.07
Sunshade 2014-02 7.311 187.07
Sunshade 2014-03 20.079 187.07
Sunshade 2014-04 28.579 187.07
Sunshade 2014-05 17.494 187.07
Sunshade 2014-06 40.065 187.07
Sunshade 2014-07 37.348 187.07
Sunshade 2014-08 12.924 187.07
Sunshade 2014-09 8.564 187.07
Sunshade 2014-10 7.844 187.07
Sunshade 2014-11 2.766 187.07
Sunshade 2014-12 2.610 187.07
Compare numbers
and see if model
matches reality
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
Sales and forecast
ITEM MTH QTY TYPE QTY_YR
---------- ------- ---- -------- ------
Snowchain 2011-01 79 Actual 331
...
Snowchain 2011-12 74 Actual 331
Snowchain 2012-01 148 Actual 582
...
Snowchain 2012-12 172 Actual 582
Snowchain 2013-01 167 Actual 691
...
Snowchain 2013-12 160 Actual 691
Snowchain 2014-01 222 Forecast 825
Snowchain 2014-02 325 Forecast 825
Snowchain 2014-03 51 Forecast 825
Snowchain 2014-04 2 Forecast 825
Snowchain 2014-05 0 Forecast 825
Snowchain 2014-06 0 Forecast 825
Snowchain 2014-07 0 Forecast 825
Snowchain 2014-08 1 Forecast 825
Snowchain 2014-09 1 Forecast 825
Snowchain 2014-10 6 Forecast 825
Snowchain 2014-11 26 Forecast 825
Snowchain 2014-12 191 Forecast 825
ITEM MTH QTY TYPE QTY_YR
---------- ------- ---- -------- ------
Sunshade 2011-01 4 Actual 377
...
Sunshade 2011-12 8 Actual 377
Sunshade 2012-01 2 Actual 321
...
Sunshade 2012-12 3 Actual 321
Sunshade 2013-01 2 Actual 263
...
Sunshade 2013-12 5 Actual 263
Sunshade 2014-01 1 Forecast 187
Sunshade 2014-02 7 Forecast 187
Sunshade 2014-03 20 Forecast 187
Sunshade 2014-04 29 Forecast 187
Sunshade 2014-05 17 Forecast 187
Sunshade 2014-06 40 Forecast 187
Sunshade 2014-07 37 Forecast 187
Sunshade 2014-08 13 Forecast 187
Sunshade 2014-09 9 Forecast 187
Sunshade 2014-10 8 Forecast 187
Sunshade 2014-11 3 Forecast 187
Sunshade 2014-12 3 Forecast 187
This model also
follows up/down
trends
Time series slightly more conservative…
…probably
due to
negative
forecasts
in simple
model
…than simple transpose model…
 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
 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

More Related Content

More from Kim Berg Hansen

External Tables - not just loading a csv file
External Tables - not just loading a csv fileExternal Tables - not just loading a csv file
External Tables - not just loading a csv file
Kim Berg Hansen
 
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
Kim Berg Hansen
 
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and suchWhen 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
Kim Berg Hansen
 
Analytic Views in Oracle 12.2
Analytic Views in Oracle 12.2Analytic Views in Oracle 12.2
Analytic Views in Oracle 12.2
Kim Berg Hansen
 
Read, store and create xml and json
Read, store and create xml and jsonRead, store and create xml and json
Read, store and create xml and json
Kim Berg Hansen
 
Data twisting
Data twistingData twisting
Data twisting
Kim Berg Hansen
 
Oracle database - Get external data via HTTP, FTP and Web Services
Oracle database - Get external data via HTTP, FTP and Web ServicesOracle database - Get external data via HTTP, FTP and Web Services
Oracle database - Get external data via HTTP, FTP and Web Services
Kim Berg Hansen
 
Real cases of indispensability of Oracle SQL analytic functions
Real cases of indispensability of Oracle SQL analytic functionsReal cases of indispensability of Oracle SQL analytic functions
Real cases of indispensability of Oracle SQL analytic functions
Kim Berg Hansen
 
Really using Oracle analytic SQL functions
Really using Oracle analytic SQL functionsReally using Oracle analytic SQL functions
Really using Oracle analytic SQL functions
Kim Berg Hansen
 

More from Kim Berg Hansen (9)

External Tables - not just loading a csv file
External Tables - not just loading a csv fileExternal Tables - not just loading a csv file
External Tables - not just loading a csv file
 
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
When 7 bit-ascii ain't enough - about NLS, collation, charsets, unicode and s...
 
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and suchWhen 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
When 7-bit ASCII ain't enough - about NLS, Collation, Charsets, Unicode and such
 
Analytic Views in Oracle 12.2
Analytic Views in Oracle 12.2Analytic Views in Oracle 12.2
Analytic Views in Oracle 12.2
 
Read, store and create xml and json
Read, store and create xml and jsonRead, store and create xml and json
Read, store and create xml and json
 
Data twisting
Data twistingData twisting
Data twisting
 
Oracle database - Get external data via HTTP, FTP and Web Services
Oracle database - Get external data via HTTP, FTP and Web ServicesOracle database - Get external data via HTTP, FTP and Web Services
Oracle database - Get external data via HTTP, FTP and Web Services
 
Real cases of indispensability of Oracle SQL analytic functions
Real cases of indispensability of Oracle SQL analytic functionsReal cases of indispensability of Oracle SQL analytic functions
Real cases of indispensability of Oracle SQL analytic functions
 
Really using Oracle analytic SQL functions
Really using Oracle analytic SQL functionsReally using Oracle analytic SQL functions
Really using Oracle analytic SQL functions
 

Recently uploaded

Predicting Product Ad Campaign Performance: A Data Analysis Project Presentation
Predicting Product Ad Campaign Performance: A Data Analysis Project PresentationPredicting Product Ad Campaign Performance: A Data Analysis Project Presentation
Predicting Product Ad Campaign Performance: A Data Analysis Project Presentation
Boston Institute of Analytics
 
Influence of Marketing Strategy and Market Competition on Business Plan
Influence of Marketing Strategy and Market Competition on Business PlanInfluence of Marketing Strategy and Market Competition on Business Plan
Influence of Marketing Strategy and Market Competition on Business Plan
jerlynmaetalle
 
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
mbawufebxi
 
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptxData_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
AnirbanRoy608946
 
Criminal IP - Threat Hunting Webinar.pdf
Criminal IP - Threat Hunting Webinar.pdfCriminal IP - Threat Hunting Webinar.pdf
Criminal IP - Threat Hunting Webinar.pdf
Criminal IP
 
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdfSample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
Linda486226
 
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
ewymefz
 
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
Subhajit Sahu
 
standardisation of garbhpala offhgfffghh
standardisation of garbhpala offhgfffghhstandardisation of garbhpala offhgfffghh
standardisation of garbhpala offhgfffghh
ArpitMalhotra16
 
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
dwreak4tg
 
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
2023240532
 
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
ahzuo
 
Machine learning and optimization techniques for electrical drives.pptx
Machine learning and optimization techniques for electrical drives.pptxMachine learning and optimization techniques for electrical drives.pptx
Machine learning and optimization techniques for electrical drives.pptx
balafet
 
一比一原版(NYU毕业证)纽约大学毕业证成绩单
一比一原版(NYU毕业证)纽约大学毕业证成绩单一比一原版(NYU毕业证)纽约大学毕业证成绩单
一比一原版(NYU毕业证)纽约大学毕业证成绩单
ewymefz
 
FP Growth Algorithm and its Applications
FP Growth Algorithm and its ApplicationsFP Growth Algorithm and its Applications
FP Growth Algorithm and its Applications
MaleehaSheikh2
 
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
ukgaet
 
The affect of service quality and online reviews on customer loyalty in the E...
The affect of service quality and online reviews on customer loyalty in the E...The affect of service quality and online reviews on customer loyalty in the E...
The affect of service quality and online reviews on customer loyalty in the E...
jerlynmaetalle
 
Opendatabay - Open Data Marketplace.pptx
Opendatabay - Open Data Marketplace.pptxOpendatabay - Open Data Marketplace.pptx
Opendatabay - Open Data Marketplace.pptx
Opendatabay
 
一比一原版(QU毕业证)皇后大学毕业证成绩单
一比一原版(QU毕业证)皇后大学毕业证成绩单一比一原版(QU毕业证)皇后大学毕业证成绩单
一比一原版(QU毕业证)皇后大学毕业证成绩单
enxupq
 
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
John Andrews
 

Recently uploaded (20)

Predicting Product Ad Campaign Performance: A Data Analysis Project Presentation
Predicting Product Ad Campaign Performance: A Data Analysis Project PresentationPredicting Product Ad Campaign Performance: A Data Analysis Project Presentation
Predicting Product Ad Campaign Performance: A Data Analysis Project Presentation
 
Influence of Marketing Strategy and Market Competition on Business Plan
Influence of Marketing Strategy and Market Competition on Business PlanInfluence of Marketing Strategy and Market Competition on Business Plan
Influence of Marketing Strategy and Market Competition on Business Plan
 
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
一比一原版(Bradford毕业证书)布拉德福德大学毕业证如何办理
 
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptxData_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
Data_and_Analytics_Essentials_Architect_an_Analytics_Platform.pptx
 
Criminal IP - Threat Hunting Webinar.pdf
Criminal IP - Threat Hunting Webinar.pdfCriminal IP - Threat Hunting Webinar.pdf
Criminal IP - Threat Hunting Webinar.pdf
 
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdfSample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
Sample_Global Non-invasive Prenatal Testing (NIPT) Market, 2019-2030.pdf
 
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
一比一原版(UofM毕业证)明尼苏达大学毕业证成绩单
 
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
Levelwise PageRank with Loop-Based Dead End Handling Strategy : SHORT REPORT ...
 
standardisation of garbhpala offhgfffghh
standardisation of garbhpala offhgfffghhstandardisation of garbhpala offhgfffghh
standardisation of garbhpala offhgfffghh
 
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
一比一原版(BCU毕业证书)伯明翰城市大学毕业证如何办理
 
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
Quantitative Data AnalysisReliability Analysis (Cronbach Alpha) Common Method...
 
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
一比一原版(UIUC毕业证)伊利诺伊大学|厄巴纳-香槟分校毕业证如何办理
 
Machine learning and optimization techniques for electrical drives.pptx
Machine learning and optimization techniques for electrical drives.pptxMachine learning and optimization techniques for electrical drives.pptx
Machine learning and optimization techniques for electrical drives.pptx
 
一比一原版(NYU毕业证)纽约大学毕业证成绩单
一比一原版(NYU毕业证)纽约大学毕业证成绩单一比一原版(NYU毕业证)纽约大学毕业证成绩单
一比一原版(NYU毕业证)纽约大学毕业证成绩单
 
FP Growth Algorithm and its Applications
FP Growth Algorithm and its ApplicationsFP Growth Algorithm and its Applications
FP Growth Algorithm and its Applications
 
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
一比一原版(UVic毕业证)维多利亚大学毕业证成绩单
 
The affect of service quality and online reviews on customer loyalty in the E...
The affect of service quality and online reviews on customer loyalty in the E...The affect of service quality and online reviews on customer loyalty in the E...
The affect of service quality and online reviews on customer loyalty in the E...
 
Opendatabay - Open Data Marketplace.pptx
Opendatabay - Open Data Marketplace.pptxOpendatabay - Open Data Marketplace.pptx
Opendatabay - Open Data Marketplace.pptx
 
一比一原版(QU毕业证)皇后大学毕业证成绩单
一比一原版(QU毕业证)皇后大学毕业证成绩单一比一原版(QU毕业证)皇后大学毕业证成绩单
一比一原版(QU毕业证)皇后大学毕业证成绩单
 
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
Chatty Kathy - UNC Bootcamp Final Project Presentation - Final Version - 5.23...
 

Oracle database - Analytic functions - Advanced cases

  • 1. Analytic Functions Advanced Cases Kim Berg Hansen T. Hansen Gruppen A/S
  • 2. Picking by FIFO  Sales forecasting Cases
  • 3.  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
  • 5. 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'); Inventory loc = 1-A-20 1 = warehouse A = aisle 20 = position
  • 6. 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 order 24 Ale 18 Bock
  • 7. Join up select 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 18 Ale 24 2-A-02 2014-02-02 24 Ale 24 1-C-05 2014-02-03 18 Ale 24 2-D-07 2014-02-04 9 Ale 24 1-A-31 2014-02-05 12 Bock 18 1-B-15 2014-02-02 2 Bock 18 1-C-04 2014-02-03 12 Bock 18 2-D-23 2014-02-04 1 Bock 18 1-B-11 2014-02-05 4 Bock 18 1-A-02 2014-02-06 18 Order locations for each item by purchase date Visually easy to see what we need to pick 18 Ale of the oldest and 6 of the next and so on
  • 8. 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 sum Partition for each item Order by date Rolling sum of all previous rows
  • 9. ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY ----- ------- ------- ---------- ------- ----------- Ale 24 1-A-20 2014-02-01 18 Ale 24 2-A-02 2014-02-02 24 18 Ale 24 1-C-05 2014-02-03 18 42 Ale 24 2-D-07 2014-02-04 9 60 Ale 24 1-A-31 2014-02-05 12 69 Bock 18 1-B-15 2014-02-02 2 Bock 18 1-C-04 2014-02-03 12 2 Bock 18 2-D-23 2014-02-04 1 14 Bock 18 1-B-11 2014-02-05 4 15 Bock 18 1-A-02 2014-02-06 18 19 Rolling sum of previous Each row can now evaluate if sufficient has been picked If the sum of all previous rows is less than the ordered quantity we still need to pick something and the row is needed
  • 10. 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
  • 11. ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY PICK_QTY ----- ------- ------- ---------- ------- ----------- -------- Ale 24 1-A-20 2014-02-01 18 0 18 Ale 24 2-A-02 2014-02-02 24 18 6 Bock 18 1-B-15 2014-02-02 2 0 2 Bock 18 1-C-04 2014-02-03 12 2 12 Bock 18 2-D-23 2014-02-04 1 14 1 Bock 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)
  • 12. Picklist – FIFO 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 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 18 1-B-11 Bock 3 1-B-15 Bock 2 1-C-04 Bock 12 2-A-02 Ale 6 2-D-23 Bock 1 Simple FIFO picklist Item and quantity to pick By location order
  • 13. Picklist – Shortest route 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.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 18 1-A-20 Ale 18 1-A-31 Ale 6 Switch picking strategy = Switch analytic order by Keep "outer" order by
  • 14. Picklist – Least number of picks 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.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 18 2-A-02 Ale 24 Switch picking strategy = Switch analytic order by Keep "outer" order by
  • 15. Picklist – Clean out small quantities 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.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 3 1-A-31 Ale 12 1-B-11 Bock 4 1-B-15 Bock 2 1-C-04 Bock 11 2-D-07 Ale 9 2-D-23 Bock 1 Switch picking strategy = Switch analytic order by Keep "outer" order by
  • 16. Not the greatest picking route Strategy "Clean out small quantities" give most number of picks We use that for picking route demonstration
  • 17. 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
  • 18. 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
  • 19. 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
  • 20. 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
  • 21. 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"
  • 22. 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
  • 24. 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
  • 25. 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 counter So the first aisle in each warehouse starts by 1 and therefore is odd and positions ordered ascending
  • 26. Restart count if only one door
  • 27. 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
  • 28. 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
  • 29. LOC ITEM PICK_QTY ------- ----- -------- 1-A-02 Bock 5 1-A-20 Ale 18 1-B-11 Bock 4 1-B-15 Bock 2 1-C-04 Bock 12 1-C-05 Ale 6 2-A-02 Ale 24 2-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?
  • 30. 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
  • 31. LOC ITEM PICK_QTY FROM_QTY TO_QTY ------- ----- -------- -------- ------ 1-A-20 Ale 18 1 18 2-A-02 Ale 24 19 42 1-C-05 Ale 6 43 48 1-B-15 Bock 2 1 2 1-C-04 Bock 12 3 14 2-D-23 Bock 1 15 15 1-B-11 Bock 4 16 19 1-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
  • 32. 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
  • 33. 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
  • 34. 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 >>>
  • 35. ... ), 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 >>>
  • 36. ... ) 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
  • 37. 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 24 2-A-02 Ale 2014-02-02 24 19 42 51 24 1 24 2-A-02 Ale 2014-02-02 24 19 42 62 8 25 32 2-A-02 Ale 2014-02-02 24 19 42 73 16 33 48 1-C-05 Ale 2014-02-03 6 43 48 73 16 33 48 1-B-15 Bock 2014-02-02 2 1 2 51 18 1 18 1-C-04 Bock 2014-02-03 12 3 14 51 18 1 18 2-D-23 Bock 2014-02-04 1 15 15 51 18 1 18 1-B-11 Bock 2014-02-05 4 16 19 51 18 1 18 1-B-11 Bock 2014-02-05 4 16 19 73 6 19 24 1-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 Ale That overlaps with all three orders, as they get respectively number 1-24, 25-32 and 33-48 of the 48 Ale
  • 38. 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
  • 39. 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 18 2-A-02 Ale 2014-02-02 24 19 42 51 24 1 24 6 2-A-02 Ale 2014-02-02 24 19 42 62 8 25 32 8 2-A-02 Ale 2014-02-02 24 19 42 73 16 33 48 10 1-C-05 Ale 2014-02-03 6 43 48 73 16 33 48 6 1-B-15 Bock 2014-02-02 2 1 2 51 18 1 18 2 1-C-04 Bock 2014-02-03 12 3 14 51 18 1 18 12 2-D-23 Bock 2014-02-04 1 15 15 51 18 1 18 1 1-B-11 Bock 2014-02-05 4 16 19 51 18 1 18 3 1-B-11 Bock 2014-02-05 4 16 19 73 6 19 24 1 1-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
  • 40. Batch picklist – FIFO with 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
  • 41. 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 route Named subquery with batch picklist adding warehouse, aisle, position and dense rank >>>
  • 42. 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 subquery Ordering with odd / even logic Batch picklist by FIFO with picking route in single SQL finished 
  • 43.  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
  • 44.  Picking by FIFO Sales forecasting Cases
  • 45.  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
  • 46. 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 sales 2011 - 2013 Snowchain and Sunshade
  • 47. Snowchain peaks wintertime and trends upwards Sunshade peaks summertime and trends downwards
  • 48. 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)
  • 49. Moving slope ITEM MTH QTY SLOPE ---------- ------- ---- ------- Snowchain 2011-01 79 Snowchain 2011-02 133 54.000 Snowchain 2011-03 24 -27.500 Snowchain 2011-04 1 -34.300 Snowchain 2011-05 0 -29.000 Snowchain 2011-06 0 -23.343 ... Snowchain 2013-01 167 1.776 Snowchain 2013-02 247 4.821 Snowchain 2013-03 42 4.533 Snowchain 2013-04 0 3.558 Snowchain 2013-05 0 2.574 Snowchain 2013-06 0 1.590 Snowchain 2013-07 0 .605 Snowchain 2013-08 1 -.369 Snowchain 2013-09 0 -1.343 Snowchain 2013-10 1 -2.274 Snowchain 2013-11 73 -2.363 Snowchain 2013-12 160 -.991 ITEM MTH QTY SLOPE ---------- ------- ---- ------- Sunshade 2011-01 4 Sunshade 2011-02 6 2.000 Sunshade 2011-03 32 14.000 Sunshade 2011-04 45 14.900 Sunshade 2011-05 62 15.500 Sunshade 2011-06 58 12.886 ... Sunshade 2013-01 2 -1.135 Sunshade 2013-02 8 -1.595 Sunshade 2013-03 28 -1.574 Sunshade 2013-04 26 -1.428 Sunshade 2013-05 23 -1.111 Sunshade 2013-06 46 -.574 Sunshade 2013-07 73 .537 Sunshade 2013-08 25 .560 Sunshade 2013-09 13 .421 Sunshade 2013-10 11 .217 Sunshade 2013-11 3 -.200 Sunshade 2013-12 5 -.574 Slopes in 2011 not very useful Slopes in 2013 based on 2 years data
  • 50. Slope of each month is not the same
  • 51. 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 month 12 * Slope is Y-increment per year Add 12 * Slope to Qty is forecast
  • 52. Transpose 12 months ITEM MTH QTY QTY_NEXT_YEAR ---------- ------- ---- ------------- Snowchain 2013-01 167 188.3130 Snowchain 2013-02 247 304.8557 Snowchain 2013-03 42 96.3913 Snowchain 2013-04 0 42.6991 Snowchain 2013-05 0 30.8870 Snowchain 2013-06 0 19.0748 Snowchain 2013-07 0 7.2626 Snowchain 2013-08 1 -3.4296 Snowchain 2013-09 0 -16.1217 Snowchain 2013-10 1 -26.2922 Snowchain 2013-11 73 44.6435 Snowchain 2013-12 160 148.1096 ITEM MTH QTY QTY_NEXT_YEAR ---------- ------- ---- ------------- Sunshade 2013-01 2 -11.6174 Sunshade 2013-02 8 -11.1374 Sunshade 2013-03 28 9.1130 Sunshade 2013-04 26 8.8609 Sunshade 2013-05 23 9.6643 Sunshade 2013-06 46 39.1130 Sunshade 2013-07 73 79.4487 Sunshade 2013-08 25 31.7148 Sunshade 2013-09 13 18.0504 Sunshade 2013-10 11 13.6087 Sunshade 2013-11 3 .5948 Sunshade 2013-12 5 -1.8870 Each month is transposed 12 months into the future by the slope of the previous 24 months
  • 53. 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 forecast Round off to whole quantities and assume negative forecast is a zero sale
  • 54. Rounded forecast ITEM MTH FORECAST ---------- ------- -------- Snowchain 2014-01 188 Snowchain 2014-02 305 Snowchain 2014-03 96 Snowchain 2014-04 43 Snowchain 2014-05 31 Snowchain 2014-06 19 Snowchain 2014-07 7 Snowchain 2014-08 0 Snowchain 2014-09 0 Snowchain 2014-10 0 Snowchain 2014-11 45 Snowchain 2014-12 148 ITEM MTH FORECAST ---------- ------- -------- Sunshade 2014-01 0 Sunshade 2014-02 0 Sunshade 2014-03 9 Sunshade 2014-04 9 Sunshade 2014-05 10 Sunshade 2014-06 39 Sunshade 2014-07 79 Sunshade 2014-08 32 Sunshade 2014-09 18 Sunshade 2014-10 14 Sunshade 2014-11 1 Sunshade 2014-12 0 Simple forecast with nice numbers
  • 55. 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
  • 56. Sales and forecast ITEM MTH QTY TYPE QTY_YR ---------- ------- ---- -------- ------ Snowchain 2011-01 79 Actual 331 ... Snowchain 2011-12 74 Actual 331 Snowchain 2012-01 148 Actual 582 ... Snowchain 2012-12 172 Actual 582 Snowchain 2013-01 167 Actual 691 ... Snowchain 2013-12 160 Actual 691 Snowchain 2014-01 188 Forecast 882 Snowchain 2014-02 305 Forecast 882 Snowchain 2014-03 96 Forecast 882 Snowchain 2014-04 43 Forecast 882 Snowchain 2014-05 31 Forecast 882 Snowchain 2014-06 19 Forecast 882 Snowchain 2014-07 7 Forecast 882 Snowchain 2014-08 0 Forecast 882 Snowchain 2014-09 0 Forecast 882 Snowchain 2014-10 0 Forecast 882 Snowchain 2014-11 45 Forecast 882 Snowchain 2014-12 148 Forecast 882 ITEM MTH QTY TYPE QTY_YR ---------- ------- ---- -------- ------ Sunshade 2011-01 4 Actual 377 ... Sunshade 2011-12 8 Actual 377 Sunshade 2012-01 2 Actual 321 ... Sunshade 2012-12 3 Actual 321 Sunshade 2013-01 2 Actual 263 ... Sunshade 2013-12 5 Actual 263 Sunshade 2014-01 0 Forecast 211 Sunshade 2014-02 0 Forecast 211 Sunshade 2014-03 9 Forecast 211 Sunshade 2014-04 9 Forecast 211 Sunshade 2014-05 10 Forecast 211 Sunshade 2014-06 39 Forecast 211 Sunshade 2014-07 79 Forecast 211 Sunshade 2014-08 32 Forecast 211 Sunshade 2014-09 18 Forecast 211 Sunshade 2014-10 14 Forecast 211 Sunshade 2014-11 1 Forecast 211 Sunshade 2014-12 0 Forecast 211 Year totals easy to see trend Snowchain upward, Sunshade downward
  • 57. Forecast follow trend up or down but keep graph shape (seasons)
  • 58.  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
  • 59. 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
  • 60. Time Series ITEM TS MTH YR MTHNO QTY ---------- --- ------- ----- ----- ---- Snowchain 1 2011-01 2011 1 79 Snowchain 2 2011-02 2011 2 133 Snowchain 3 2011-03 2011 3 24 ... Snowchain 34 2013-10 2013 10 1 Snowchain 35 2013-11 2013 11 73 Snowchain 36 2013-12 2013 12 160 Snowchain 37 2014-01 2014 1 Snowchain 38 2014-02 2014 2 Snowchain 39 2014-03 2014 3 Snowchain 40 2014-04 2014 4 Snowchain 41 2014-05 2014 5 Snowchain 42 2014-06 2014 6 Snowchain 43 2014-07 2014 7 Snowchain 44 2014-08 2014 8 Snowchain 45 2014-09 2014 9 Snowchain 46 2014-10 2014 10 Snowchain 47 2014-11 2014 11 Snowchain 48 2014-12 2014 12 ITEM TS MTH YR MTHNO QTY ---------- --- ------- ----- ----- ---- Sunshade 1 2011-01 2011 1 4 Sunshade 2 2011-02 2011 2 6 Sunshade 3 2011-03 2011 3 32 ... Sunshade 34 2013-10 2013 10 11 Sunshade 35 2013-11 2013 11 3 Sunshade 36 2013-12 2013 12 5 Sunshade 37 2014-01 2014 1 Sunshade 38 2014-02 2014 2 Sunshade 39 2014-03 2014 3 Sunshade 40 2014-04 2014 4 Sunshade 41 2014-05 2014 5 Sunshade 42 2014-06 2014 6 Sunshade 43 2014-07 2014 7 Sunshade 44 2014-08 2014 8 Sunshade 45 2014-09 2014 9 Sunshade 46 2014-10 2014 10 Sunshade 47 2014-11 2014 11 Sunshade 48 2014-12 2014 12 Sales in 2011-13, null qty in 2014
  • 61. 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 months Average of those two is CMA Do this only for those months (ts 7-30) where there is 12 months data
  • 62. Centered Moving Average ITEM TS MTH YR MTHNO QTY CMA ---------- --- ------- ----- ----- ---- ------- Snowchain 1 2011-01 2011 1 79 Snowchain 2 2011-02 2011 2 133 Snowchain 3 2011-03 2011 3 24 Snowchain 4 2011-04 2011 4 1 Snowchain 5 2011-05 2011 5 0 Snowchain 6 2011-06 2011 6 0 Snowchain 7 2011-07 2011 7 0 30.458 Snowchain 8 2011-08 2011 8 0 36.500 Snowchain 9 2011-09 2011 9 1 39.917 Snowchain 10 2011-10 2011 10 4 40.208 Snowchain 11 2011-11 2011 11 15 40.250 Snowchain 12 2011-12 2011 12 74 40.250 Snowchain 13 2012-01 2012 1 148 40.250 Snowchain 14 2012-02 2012 2 209 40.292 Snowchain 15 2012-03 2012 3 30 40.292 ... Snowchain 29 2013-05 2013 5 0 56.250 Snowchain 30 2013-06 2013 6 0 58.083 Snowchain 31 2013-07 2013 7 0 Snowchain 32 2013-08 2013 8 1 Snowchain 33 2013-09 2013 9 0 ... ITEM TS MTH YR MTHNO QTY CMA ---------- --- ------- ----- ----- ---- ------- Sunshade 1 2011-01 2011 1 4 Sunshade 2 2011-02 2011 2 6 Sunshade 3 2011-03 2011 3 32 Sunshade 4 2011-04 2011 4 45 Sunshade 5 2011-05 2011 5 62 Sunshade 6 2011-06 2011 6 58 Sunshade 7 2011-07 2011 7 85 31.333 Sunshade 8 2011-08 2011 8 28 31.542 Sunshade 9 2011-09 2011 9 24 31.708 Sunshade 10 2011-10 2011 10 19 32.208 Sunshade 11 2011-11 2011 11 6 31.458 Sunshade 12 2011-12 2011 12 8 30.917 Sunshade 13 2012-01 2012 1 2 30.542 Sunshade 14 2012-02 2012 2 13 29.083 Sunshade 15 2012-03 2012 3 29 28.292 ... Sunshade 29 2013-05 2013 5 23 21.833 Sunshade 30 2013-06 2013 6 46 21.833 Sunshade 31 2013-07 2013 7 73 Sunshade 32 2013-08 2013 8 25 Sunshade 33 2013-09 2013 9 13 ... CMA for 24 months 2011-07 to 2013-06
  • 63. 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 seasonality Partition makes seasonality same for all january, all february, etc. (by item)
  • 64. Seasonality factor ITEM TS MTH YR MTHNO QTY CMA S ---------- --- ------- ----- ----- ---- ------- ------- ... Snowchain 9 2011-09 2011 9 1 39.917 .0125 Snowchain 10 2011-10 2011 10 4 40.208 .0774 Snowchain 11 2011-11 2011 11 15 40.250 .3435 Snowchain 12 2011-12 2011 12 74 40.250 2.5094 Snowchain 13 2012-01 2012 1 148 40.250 3.3824 Snowchain 14 2012-02 2012 2 209 40.292 4.8771 Snowchain 15 2012-03 2012 3 30 40.292 .7606 Snowchain 16 2012-04 2012 4 2 40.208 .0249 Snowchain 17 2012-05 2012 5 0 40.250 .0000 Snowchain 18 2012-06 2012 6 0 44.417 .0000 Snowchain 19 2012-07 2012 7 0 49.292 .0000 Snowchain 20 2012-08 2012 8 1 51.667 .0097 Snowchain 21 2012-09 2012 9 0 53.750 .0125 Snowchain 22 2012-10 2012 10 3 54.167 .0774 Snowchain 23 2012-11 2012 11 17 54.083 .3435 Snowchain 24 2012-12 2012 12 172 54.083 2.5094 Snowchain 25 2013-01 2013 1 167 54.083 3.3824 Snowchain 26 2013-02 2013 2 247 54.083 4.8771 Snowchain 27 2013-03 2013 3 42 54.083 .7606 Snowchain 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 .5876 Sunshade 10 2011-10 2011 10 19 32.208 .5567 Sunshade 11 2011-11 2011 11 6 31.458 .2033 Sunshade 12 2011-12 2011 12 8 30.917 .1989 Sunshade 13 2012-01 2012 1 2 30.542 .0805 Sunshade 14 2012-02 2012 2 13 29.083 .4071 Sunshade 15 2012-03 2012 3 29 28.292 1.1489 Sunshade 16 2012-04 2012 4 60 27.500 1.6818 Sunshade 17 2012-05 2012 5 29 27.208 1.0596 Sunshade 18 2012-06 2012 6 78 26.958 2.5001 Sunshade 19 2012-07 2012 7 56 26.750 2.4031 Sunshade 20 2012-08 2012 8 22 26.542 .8583 Sunshade 21 2012-09 2012 9 11 26.292 .5876 Sunshade 22 2012-10 2012 10 13 24.833 .5567 Sunshade 23 2012-11 2012 11 5 23.167 .2033 Sunshade 24 2012-12 2012 12 3 21.583 .1989 Sunshade 25 2013-01 2013 1 2 20.958 .0805 Sunshade 26 2013-02 2013 2 8 21.792 .4071 Sunshade 27 2013-03 2013 3 28 22.000 1.1489 Sunshade 28 2013-04 2013 4 26 22.000 1.6818 ... S calculated for each month Partition makes it repeat
  • 65. 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
  • 66. Deseasonalized quantity ITEM TS MTH QTY CMA S DES ---------- --- ------- ---- ------- ------- -------- Snowchain 1 2011-01 79 3.3824 23.356 Snowchain 2 2011-02 133 4.8771 27.270 Snowchain 3 2011-03 24 .7606 31.555 ... Snowchain 22 2012-10 3 54.167 .0774 38.743 Snowchain 23 2012-11 17 54.083 .3435 49.490 Snowchain 24 2012-12 172 54.083 2.5094 68.542 Snowchain 25 2013-01 167 54.083 3.3824 49.373 Snowchain 26 2013-02 247 54.083 4.8771 50.645 Snowchain 27 2013-03 42 54.083 .7606 55.221 Snowchain 28 2013-04 0 54.000 .0249 .004 Snowchain 29 2013-05 0 56.250 .0000 46.924 Snowchain 30 2013-06 0 58.083 .0000 50.339 Snowchain 31 2013-07 0 .0000 37.651 Snowchain 32 2013-08 1 .0097 103.319 Snowchain 33 2013-09 0 .0125 .008 Snowchain 34 2013-10 1 .0774 12.914 Snowchain 35 2013-11 73 .3435 212.518 Snowchain 36 2013-12 160 2.5094 63.760 ... ITEM TS MTH QTY CMA S DES ---------- --- ------- ---- ------- ------- -------- Sunshade 1 2011-01 4 .0805 49.717 Sunshade 2 2011-02 6 .4071 14.740 Sunshade 3 2011-03 32 1.1489 27.853 ... Sunshade 22 2012-10 13 24.833 .5567 23.352 Sunshade 23 2012-11 5 23.167 .2033 24.597 Sunshade 24 2012-12 3 21.583 .1989 15.085 Sunshade 25 2013-01 2 20.958 .0805 24.858 Sunshade 26 2013-02 8 21.792 .4071 19.654 Sunshade 27 2013-03 28 22.000 1.1489 24.372 Sunshade 28 2013-04 26 22.000 1.6818 15.459 Sunshade 29 2013-05 23 21.833 1.0596 21.705 Sunshade 30 2013-06 46 21.833 2.5001 18.399 Sunshade 31 2013-07 73 2.4031 30.377 Sunshade 32 2013-08 25 .8583 29.127 Sunshade 33 2013-09 13 .5876 22.122 Sunshade 34 2013-10 11 .5567 19.759 Sunshade 35 2013-11 3 .2033 14.758 Sunshade 36 2013-12 5 .1989 25.141 ... DES show trend more clear without seasonal up/down
  • 68. 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 axis Add slope*time series (month) and get Y value of trend line
  • 69. Trend (regression) ITEM TS MTH QTY CMA S DES T ---------- --- ------- ---- ------- ------- -------- ------- Snowchain 1 2011-01 79 3.3824 23.356 32.163 Snowchain 2 2011-02 133 4.8771 27.270 33.096 Snowchain 3 2011-03 24 .7606 31.555 34.030 ... Snowchain 34 2013-10 1 .0774 12.914 62.976 Snowchain 35 2013-11 73 .3435 212.518 63.910 Snowchain 36 2013-12 160 2.5094 63.760 64.844 Snowchain 37 2014-01 3.3824 65.777 Snowchain 38 2014-02 4.8771 66.711 Snowchain 39 2014-03 .7606 67.645 Snowchain 40 2014-04 .0249 68.579 Snowchain 41 2014-05 .0000 69.512 Snowchain 42 2014-06 .0000 70.446 Snowchain 43 2014-07 .0000 71.380 Snowchain 44 2014-08 .0097 72.314 Snowchain 45 2014-09 .0125 73.247 Snowchain 46 2014-10 .0774 74.181 Snowchain 47 2014-11 .3435 75.115 Snowchain 48 2014-12 2.5094 76.049 ITEM TS MTH QTY CMA S DES T ---------- --- ------- ---- ------- ------- -------- ------- Sunshade 1 2011-01 4 .0805 49.717 35.860 Sunshade 2 2011-02 6 .4071 14.740 35.376 Sunshade 3 2011-03 32 1.1489 27.853 34.892 ... Sunshade 34 2013-10 11 .5567 19.759 19.895 Sunshade 35 2013-11 3 .2033 14.758 19.412 Sunshade 36 2013-12 5 .1989 25.141 18.928 Sunshade 37 2014-01 .0805 18.444 Sunshade 38 2014-02 .4071 17.960 Sunshade 39 2014-03 1.1489 17.477 Sunshade 40 2014-04 1.6818 16.993 Sunshade 41 2014-05 1.0596 16.509 Sunshade 42 2014-06 2.5001 16.025 Sunshade 43 2014-07 2.4031 15.542 Sunshade 44 2014-08 .8583 15.058 Sunshade 45 2014-09 .5876 14.574 Sunshade 46 2014-10 .5567 14.090 Sunshade 47 2014-11 .2033 13.606 Sunshade 48 2014-12 .1989 13.123 Linear trend line from 2011 to 2014
  • 71. 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
  • 72. Reseasonalize (forecast) ITEM TS MTH QTY S DES T FORECAST ---------- --- ------- ---- ------- -------- ------- -------- Snowchain 1 2011-01 79 3.3824 23.356 32.163 108.788 Snowchain 2 2011-02 133 4.8771 27.270 33.096 161.414 Snowchain 3 2011-03 24 .7606 31.555 34.030 25.882 Snowchain 4 2011-04 1 .0249 40.207 34.964 .870 ... Snowchain 33 2013-09 0 .0125 .008 62.042 .777 Snowchain 34 2013-10 1 .0774 12.914 62.976 4.876 Snowchain 35 2013-11 73 .3435 212.518 63.910 21.953 Snowchain 36 2013-12 160 2.5094 63.760 64.844 162.718 Snowchain 37 2014-01 3.3824 65.777 222.487 Snowchain 38 2014-02 4.8771 66.711 325.357 Snowchain 39 2014-03 .7606 67.645 51.449 Snowchain 40 2014-04 .0249 68.579 1.706 Snowchain 41 2014-05 .0000 69.512 .000 Snowchain 42 2014-06 .0000 70.446 .000 Snowchain 43 2014-07 .0000 71.380 .000 Snowchain 44 2014-08 .0097 72.314 .700 Snowchain 45 2014-09 .0125 73.247 .918 Snowchain 46 2014-10 .0774 74.181 5.744 Snowchain 47 2014-11 .3435 75.115 25.802 Snowchain 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.885 Sunshade 2 2011-02 6 .4071 14.740 35.376 14.400 Sunshade 3 2011-03 32 1.1489 27.853 34.892 40.087 Sunshade 4 2011-04 45 1.6818 26.757 34.409 57.869 ... Sunshade 33 2013-09 13 .5876 22.122 20.379 11.976 Sunshade 34 2013-10 11 .5567 19.759 19.895 11.076 Sunshade 35 2013-11 3 .2033 14.758 19.412 3.946 Sunshade 36 2013-12 5 .1989 25.141 18.928 3.764 Sunshade 37 2014-01 .0805 18.444 1.484 Sunshade 38 2014-02 .4071 17.960 7.311 Sunshade 39 2014-03 1.1489 17.477 20.079 Sunshade 40 2014-04 1.6818 16.993 28.579 Sunshade 41 2014-05 1.0596 16.509 17.494 Sunshade 42 2014-06 2.5001 16.025 40.065 Sunshade 43 2014-07 2.4031 15.542 37.348 Sunshade 44 2014-08 .8583 15.058 12.924 Sunshade 45 2014-09 .5876 14.574 8.564 Sunshade 46 2014-10 .5567 14.090 7.844 Sunshade 47 2014-11 .2033 13.606 2.766 Sunshade 48 2014-12 .1989 13.123 2.610 Trend line linear Apply seasonality = forecast
  • 75. 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
  • 76. 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.57 Snowchain 2013-02 247 270.710 691 690.57 Snowchain 2013-03 42 42.927 691 690.57 Snowchain 2013-04 0 1.427 691 690.57 ... Snowchain 2013-09 0 .777 691 690.57 Snowchain 2013-10 1 4.876 691 690.57 Snowchain 2013-11 73 21.953 691 690.57 Snowchain 2013-12 160 162.718 691 690.57 Snowchain 2014-01 222.487 825.00 Snowchain 2014-02 325.357 825.00 Snowchain 2014-03 51.449 825.00 Snowchain 2014-04 1.706 825.00 Snowchain 2014-05 .000 825.00 Snowchain 2014-06 .000 825.00 Snowchain 2014-07 .000 825.00 Snowchain 2014-08 .700 825.00 Snowchain 2014-09 .918 825.00 Snowchain 2014-10 5.744 825.00 Snowchain 2014-11 25.802 825.00 Snowchain 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.91 Sunshade 2013-05 23 23.645 263 254.91 Sunshade 2013-06 46 54.579 263 254.91 Sunshade 2013-07 73 51.299 263 254.91 Sunshade 2013-08 25 17.907 263 254.91 ... Sunshade 2013-11 3 3.946 263 254.91 Sunshade 2013-12 5 3.764 263 254.91 Sunshade 2014-01 1.484 187.07 Sunshade 2014-02 7.311 187.07 Sunshade 2014-03 20.079 187.07 Sunshade 2014-04 28.579 187.07 Sunshade 2014-05 17.494 187.07 Sunshade 2014-06 40.065 187.07 Sunshade 2014-07 37.348 187.07 Sunshade 2014-08 12.924 187.07 Sunshade 2014-09 8.564 187.07 Sunshade 2014-10 7.844 187.07 Sunshade 2014-11 2.766 187.07 Sunshade 2014-12 2.610 187.07 Compare numbers and see if model matches reality
  • 77. 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
  • 78. Sales and forecast ITEM MTH QTY TYPE QTY_YR ---------- ------- ---- -------- ------ Snowchain 2011-01 79 Actual 331 ... Snowchain 2011-12 74 Actual 331 Snowchain 2012-01 148 Actual 582 ... Snowchain 2012-12 172 Actual 582 Snowchain 2013-01 167 Actual 691 ... Snowchain 2013-12 160 Actual 691 Snowchain 2014-01 222 Forecast 825 Snowchain 2014-02 325 Forecast 825 Snowchain 2014-03 51 Forecast 825 Snowchain 2014-04 2 Forecast 825 Snowchain 2014-05 0 Forecast 825 Snowchain 2014-06 0 Forecast 825 Snowchain 2014-07 0 Forecast 825 Snowchain 2014-08 1 Forecast 825 Snowchain 2014-09 1 Forecast 825 Snowchain 2014-10 6 Forecast 825 Snowchain 2014-11 26 Forecast 825 Snowchain 2014-12 191 Forecast 825 ITEM MTH QTY TYPE QTY_YR ---------- ------- ---- -------- ------ Sunshade 2011-01 4 Actual 377 ... Sunshade 2011-12 8 Actual 377 Sunshade 2012-01 2 Actual 321 ... Sunshade 2012-12 3 Actual 321 Sunshade 2013-01 2 Actual 263 ... Sunshade 2013-12 5 Actual 263 Sunshade 2014-01 1 Forecast 187 Sunshade 2014-02 7 Forecast 187 Sunshade 2014-03 20 Forecast 187 Sunshade 2014-04 29 Forecast 187 Sunshade 2014-05 17 Forecast 187 Sunshade 2014-06 40 Forecast 187 Sunshade 2014-07 37 Forecast 187 Sunshade 2014-08 13 Forecast 187 Sunshade 2014-09 9 Forecast 187 Sunshade 2014-10 8 Forecast 187 Sunshade 2014-11 3 Forecast 187 Sunshade 2014-12 3 Forecast 187 This model also follows up/down trends
  • 79. Time series slightly more conservative…
  • 81.  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
  • 82.  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