Top Banner
Really Using Analytic Functions Kim Berg Hansen T. Hansen Gruppen A/S
154

Really using Oracle analytic SQL functions

Jan 27, 2015

Download

Technology

Kim Berg Hansen

Presentation on Oracle analytic SQL functions as I presented it on UKOUG 2012 conference in Birmingham
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Really using Oracle analytic SQL functions

Really UsingAnalytic Functions

Kim Berg HansenT. Hansen Gruppen A/S

Page 2: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions2

Who is this Kim?• a Danish SQL and PL/SQL Developer:

http://dspsd.blogspot.com• Professional geek since 1996• Oracle programmer since 2000• Single SQL Statement mantra (©Tom Kyte)• Danish Beer Enthusiast (http://ale.dk)• Likes to cook• Reads sci-fi

2012-12-05

Page 3: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions3

What’s up?• Why analytics?• Case 1: Top selling items• Case 2: Picking by FIFO• Case 3: Efficient picking route• Case 4: Picking efficiency• Case 5: Forecasting sales• Case 6: Forecast zero firework stock• Case 7: Multi-order FIFO picking (time permitting)• Any questions?

2012-12-05

Page 4: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions4

Why analytics?• Normal SQL functions operate on one row• Aggregates can do more rows but loose detail• When you need details together with subtotals, ranks, ratios,

comparisons, you could do: Client operations (tool or code with variables/arrays) Scalar subqueries (multiple access of same data) Analytic functions (often much more efficient )

• Analytics allow you to operate across the entire resultset, not just a single row

2012-12-05

Page 5: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions5

Top Selling ItemsCase 1

2012-12-05

Page 6: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions6

Top selling items• Classic task for a programmer:• Show top three by product group• Also show how big percentage they

sold of the total– Both of the total by product group– And of the grand total

2012-12-05

Page 7: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions7

Tables

2012-12-05

create table items( item varchar2(10) primary key, grp varchar2(10), name varchar2(20))/

create table sales ( item varchar2(10) references items (item), mth date, qty number)/

Items with groups

Sales per month

Page 8: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions8

Data - items

2012-12-05

insert into items values ('101010','AUTO','Brake disc'); insert into items values ('102020','AUTO','Snow chain'); insert into items values ('103030','AUTO','Sparc plug'); insert into items values ('104040','AUTO','Oil filter'); insert into items values ('105050','AUTO','Light bulb'); insert into items values ('201010','MOBILE','Handsfree'); insert into items values ('202020','MOBILE','Charger'); insert into items values ('203030','MOBILE','iGloves'); insert into items values ('204040','MOBILE','Headset'); insert into items values ('205050','MOBILE','Cover');

5 autoparts

5 mobile accessories

Page 9: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions9

Data – sales AUTO

2012-12-05

insert into sales values ('101010',date '2011-04-01',10); insert into sales values ('101010',date '2011-05-01',11); insert into sales values ('101010',date '2011-06-01',12); insert into sales values ('102020',date '2011-03-01', 7); insert into sales values ('102020',date '2011-07-01', 8); insert into sales values ('103030',date '2011-01-01', 6); insert into sales values ('103030',date '2011-02-01', 9); insert into sales values ('103030',date '2011-11-01', 4); insert into sales values ('103030',date '2011-12-01',14); insert into sales values ('104040',date '2011-08-01',22); insert into sales values ('105050',date '2011-09-01',13); insert into sales values ('105050',date '2011-10-01',15);

Sales for various months of 2011 for the autoparts

Page 10: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions10

Data – sales MOBILE

2012-12-05

insert into sales values ('201010',date '2011-04-01', 5); insert into sales values ('201010',date '2011-05-01', 6); insert into sales values ('201010',date '2011-06-01', 7); insert into sales values ('202020',date '2011-03-01',21); insert into sales values ('202020',date '2011-07-01',23); insert into sales values ('203030',date '2011-01-01', 7); insert into sales values ('203030',date '2011-02-01', 7); insert into sales values ('203030',date '2011-11-01', 6); insert into sales values ('203030',date '2011-12-01', 8); insert into sales values ('204040',date '2011-08-01',35); insert into sales values ('205050',date '2011-09-01',13); insert into sales values ('205050',date '2011-10-01',15);

Sales for various months of 2011 for the mobile accessories

Page 11: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions11

Base select

2012-12-05

select i.grp , i.item , max(i.name) name , sum(s.qty) qty from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item order by i.grp, sum(s.qty) desc, i.item

Join items and sales

Sales for 2011

Group by to get total sales for 2011 per item

Page 12: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions12

Base select

2012-12-05

GRP ITEM NAME QTY

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

AUTO 101010 Brake disc 33

AUTO 103030 Sparc plug 33

AUTO 105050 Light bulb 28

AUTO 104040 Oil filter 22

AUTO 102020 Snow chain 15

MOBILE 202020 Charger 44

MOBILE 204040 Headset 35

MOBILE 203030 iGloves 28

MOBILE 205050 Cover 28

MOBILE 201010 Handsfree 18

Couple of items in each group have identical sales

Page 13: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions13

Which TOP?

2012-12-05

select g.grp, g.item, g.name, g.qty , dense_rank() over (partition by g.grp order by g.qty desc) drnk , rank() over (partition by g.grp order by g.qty desc) rnk , row_number() over (partition by g.grp order by g.qty desc, g.item) rnum from ( select i.grp , i.item , max(i.name) name , sum(s.qty) qty from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item

) g order by g.grp, g.qty desc, g.item

Base select as inline view

Page 14: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions14

Which TOP?

2012-12-05

GRP ITEM NAME QTY DRNK RNK RNUM

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

AUTO 101010 Brake disc 33 1 1 1

AUTO 103030 Sparc plug 33 1 1 2

AUTO 105050 Light bulb 28 2 3 3

AUTO 104040 Oil filter 22 3 4 4

AUTO 102020 Snow chain 15 4 5 5

MOBILE 202020 Charger 44 1 1 1

MOBILE 204040 Headset 35 2 2 2

MOBILE 203030 iGloves 28 3 3 3

MOBILE 205050 Cover 28 3 3 4

MOBILE 201010 Handsfree 18 4 5 5

The three different functions handle ties differently

Page 15: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions15

Without inline view

2012-12-05

select i.grp , i.item , max(i.name) name , sum(s.qty) qty , dense_rank() over (partition by i.grp order by sum(s.qty) desc) drnk , rank() over (partition by i.grp order by sum(s.qty) desc) rnk , row_number() over (partition by i.grp order by sum(s.qty) desc, i.item) rnum from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item order by i.grp, sum(s.qty) desc, i.item

Analytics calculated last, so can include group by expressions as well as aggregates

Page 16: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions16

Without inline view

2012-12-05

GRP ITEM NAME QTY DRNK RNK RNUM

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

AUTO 101010 Brake disc 33 1 1 1

AUTO 103030 Sparc plug 33 1 1 2

AUTO 105050 Light bulb 28 2 3 3

AUTO 104040 Oil filter 22 3 4 4

AUTO 102020 Snow chain 15 4 5 5

MOBILE 202020 Charger 44 1 1 1

MOBILE 204040 Headset 35 2 2 2

MOBILE 203030 iGloves 28 3 3 3

MOBILE 205050 Cover 28 3 3 4

MOBILE 201010 Handsfree 18 4 5 5

Identical results

Page 17: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions17

TOP 3 – rank()

2012-12-05

select g.grp, g.item, g.name, g.qty, g.rnk from ( select i.grp , i.item , max(i.name) name , sum(s.qty) qty , rank() over (partition by i.grp order by sum(s.qty) desc)

rnk from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item) g where g.rnk <= 3 order by g.grp, g.rnk, g.item

Analytic function cannot be in where clause

So inline view and filter on the alias

Page 18: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions18

TOP 3 – rank()

2012-12-05

GRP ITEM NAME QTY RNK

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

AUTO 101010 Brake disc 33 1

AUTO 103030 Sparc plug 33 1

AUTO 105050 Light bulb 28 3

MOBILE 202020 Charger 44 1

MOBILE 204040 Headset 35 2

MOBILE 203030 iGloves 28 3

MOBILE 205050 Cover 28 3

rank() works like the olympics – two gold medals mean no silver medal

Page 19: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions19

TOP 3 – dense_rank()

2012-12-05

select g.grp, g.item, g.name, g.qty, g.rnk from ( select i.grp , i.item , max(i.name) name , sum(s.qty) qty , dense_rank() over (partition by i.grp order by sum(s.qty) desc) rnk from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item) g where g.rnk <= 3 order by g.grp, g.rnk, g.item

Page 20: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions20

TOP 3 – dense_rank()

2012-12-05

GRP ITEM NAME QTY RNK

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

AUTO 101010 Brake disc 33 1

AUTO 103030 Sparc plug 33 1

AUTO 105050 Light bulb 28 2

AUTO 104040 Oil filter 22 3

MOBILE 202020 Charger 44 1

MOBILE 204040 Headset 35 2

MOBILE 203030 iGloves 28 3

MOBILE 205050 Cover 28 3

dense_rank() also gives equal rank at ties, but does not skip ranks

Page 21: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions21

TOP 3 – row_number()

2012-12-05

select g.grp, g.item, g.name, g.qty, g.rnk from ( select i.grp , i.item , max(i.name) name , sum(s.qty) qty , row_number() over (partition by i.grp order by sum(s.qty) desc, i.item) rnk from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item) g where g.rnk <= 3 order by g.grp, g.rnk, g.item

Page 22: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions22

TOP 3 – row_number()

2012-12-05

GRP ITEM NAME QTY RNK

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

AUTO 101010 Brake disc 33 1

AUTO 103030 Sparc plug 33 2

AUTO 105050 Light bulb 28 3

MOBILE 202020 Charger 44 1

MOBILE 204040 Headset 35 2

MOBILE 203030 iGloves 28 3

row_number() just numbers consecutively

If ties, then result is ”random”, so a good idea always to use ”unique” order by

Page 23: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions23

Percent of total

2012-12-05

select g.grp, g.item, g.name, g.qty, g.rnk , round(g.g_pct,1) g_pct , round(g.t_pct,1) t_pct from ( select i.grp , i.item , max(i.name) name , sum(s.qty) qty , rank() over (partition by i.grp order by sum(s.qty) desc) rnk , 100 * ratio_to_report(sum(s.qty)) over (partition by i.grp) g_pct , 100 * ratio_to_report(sum(s.qty)) over () t_pct from items i join sales s on s.item = i.item where s.mth between date '2011-01-01' and date '2011-12-01' group by i.grp, i.item) g where g.rnk <= 3 order by g.grp, g.rnk, g.item

ratio_to_report() returns number between 0 and 1

Multiply with 100 to get percent

Page 24: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions24

Percent of total

2012-12-05

GRP ITEM NAME QTY RNK G_PCT T_PCT

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

AUTO 101010 Brake disc 33 1 25.2 11.6

AUTO 103030 Sparc plug 33 1 25.2 11.6

AUTO 105050 Light bulb 28 3 21.4 9.9

MOBILE 202020 Charger 44 1 28.8 15.5

MOBILE 204040 Headset 35 2 22.9 12.3

MOBILE 203030 iGloves 28 3 18.3 9.9

MOBILE 205050 Cover 28 3 18.3 9.9

Page 25: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions25

Top selling items• What kind of top three do you wish?

– DENSE_RANK()– RANK()– ROW_NUMBER()

• PARTITION BY groups of items

• RATIO_TO_REPORT for percentages2012-12-05

Page 26: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions26

Picking by FIFOCase 2

2012-12-05

Page 27: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions27

Picking by FIFO

• Items stored in different locations in warehouse• Pick an order by First-In First-Out principle

2012-12-05

Page 28: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions28

Tables

2012-12-05

create table inventory ( item varchar2(10), loc varchar2(10), qty number, purch date)/

create table orderline ( ordno number, item varchar2(10), qty number)/

Item, location, quantity and date of purchase

Order number, item and quantity ordered

Page 29: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions29

Data

2012-12-05

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

2 items each 5 locations various purchase dates

One order with a quantity of both items

Page 30: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions30

What to pick

2012-12-05

variable pick_order number;

begin :pick_order := 1;end;/

Picking application sets bind variable for which order to pick(Could be sales order, batch order, shop refill order)

Page 31: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions31

What can we pick

2012-12-05

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 = :pick_order order by o.item, i.purch, i.loc

Join orderline to inventory to see all that potentially can be picked

Page 32: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions32

What can we pick

2012-12-05

ITEM ORD_QTY LOC PURCH LOC_QTY

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

A1 24 1-A-20 2004-11-01 18

A1 24 2-A-02 2004-11-02 24

A1 24 1-C-05 2004-11-03 18

A1 24 2-D-07 2004-11-04 9

A1 24 1-A-31 2004-11-05 12

B1 18 1-B-15 2004-11-02 2

B1 18 1-C-04 2004-11-03 12

B1 18 2-D-23 2004-11-04 1

B1 18 1-B-11 2004-11-05 4

B1 18 1-A-02 2004-11-06 18

Visually we can see we need 18 A1 from first location and 6 from second loc.

Likewise we will empty first 3 locs of B1 and pick 3 from fourth location

Page 33: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions33

Accumulate

2012-12-05

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 current row ) sum_qty from orderline o join inventory i on i.item = o.item where o.ordno = :pick_order order by o.item, i.purch, i.loc

Let’s try to create a rolling sum of the qty for each item

Page 34: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions34

Accumulate

2012-12-05

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_QTY

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

A1 24 1-A-20 2004-11-01 18 18

A1 24 2-A-02 2004-11-02 24 42

A1 24 1-C-05 2004-11-03 18 60

A1 24 2-D-07 2004-11-04 9 69

A1 24 1-A-31 2004-11-05 12 81

B1 18 1-B-15 2004-11-02 2 2

B1 18 1-C-04 2004-11-03 12 14

B1 18 2-D-23 2004-11-04 1 15

B1 18 1-B-11 2004-11-05 4 19

B1 18 1-A-02 2004-11-06 18 37

Yup, when our sum is greater than the ordered qty, it looks like we have enough

Page 35: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions35

Filter accumulated

2012-12-05

select s.*from ( 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 current row ) sum_qty from orderline o join inventory i on i.item = o.item where o.ordno = :pick_order) s where s.sum_qty < s.ord_qty order by s.item, s.purch, s.loc

So let’s try to filter on that

Page 36: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions36

Filter accumulated

2012-12-05

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_QTY

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

A1 24 1-A-20 2004-11-01 18 18

B1 18 1-B-15 2004-11-02 2 2

B1 18 1-C-04 2004-11-03 12 14

B1 18 2-D-23 2004-11-04 1 15

FAIL!

Missing the last location for each item

Page 37: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions37

Accumulate previous

2012-12-05

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 = :pick_order order by o.item, i.purch, i.loc

One small change to our rolling sum:

Sum of rows up to but not including the current row

Page 38: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions38

Accumulate previous

2012-12-05

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY

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

A1 24 1-A-20 2004-11-01 18

A1 24 2-A-02 2004-11-02 24 18

A1 24 1-C-05 2004-11-03 18 42

A1 24 2-D-07 2004-11-04 9 60

A1 24 1-A-31 2004-11-05 12 69

B1 18 1-B-15 2004-11-02 2

B1 18 1-C-04 2004-11-03 12 2

B1 18 2-D-23 2004-11-04 1 14

B1 18 1-B-11 2004-11-05 4 15

B1 18 1-A-02 2004-11-06 18 19

As long as the sum of the previous rows are not enough, we continue

When the previous rows are sufficient, we stop

Page 39: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions39

Filter previous

2012-12-05

select s.* , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.item, s.purch, s.loc

Now we can filter correctly

nvl() for first row

least() to get qty to be picked at that location

Page 40: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions40

Filter previous

2012-12-05

ITEM ORD_QTY LOC PURCH LOC_QTY SUM_PRV_QTY PICK_QTY

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

A1 24 1-A-20 2004-11-01 18 0 18

A1 24 2-A-02 2004-11-02 24 18 6

B1 18 1-B-15 2004-11-02 2 0 2

B1 18 1-C-04 2004-11-03 12 2 12

B1 18 2-D-23 2004-11-04 1 14 1

B1 18 1-B-11 2004-11-05 4 15 3

Page 41: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions41

Picking list FIFO

2012-12-05

select s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Now order by location to make a pick list

Page 42: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions42

Picking list FIFO

2012-12-05

LOC ITEM PICK_QTY

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

1-A-20 A1 18

1-B-11 B1 3

1-B-15 B1 2

1-C-04 B1 12

2-A-02 A1 6

2-D-23 B1 1

Ready for operator to go picking

Page 43: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions43

Picking small qty

2012-12-05

select s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Change pick policy by changing order:

Here empty small quantities first to clean out locations

Page 44: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions44

Picking small qty

2012-12-05

LOC ITEM PICK_QTY

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

1-A-20 A1 3

1-A-31 A1 12

1-B-11 B1 4

1-B-15 B1 2

1-C-04 B1 11

2-D-07 A1 9

2-D-23 B1 1

Lots of picks

Will clean locations quickly for new incoming goods

Page 45: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions45

Picking few picks

2012-12-05

select s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Or policy of picking as few times as possible

Page 46: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions46

Picking few picks

2012-12-05

LOC ITEM PICK_QTY

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

1-A-02 B1 18

2-A-02 A1 24

Only two picks

But will be at expense of leaving small quantities all over warehouse

Page 47: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions47

Picking short route

2012-12-05

select s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Policy of not driving to the far warehouse if possible

Page 48: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions48

Picking short route

2012-12-05

LOC ITEM PICK_QTY

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

1-A-02 B1 18

1-A-20 A1 18

1-A-31 A1 6

All picked in the very fist aisle in the warehouse

Page 49: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions49

Picking by FIFO• SUM() by item

• Ordered by purchase date

• Rolling sum to find how much was picked by ”previous rows”

• Filter away rows where sufficient has already been picked

2012-12-05

Page 50: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions50

Efficient picking routeCase 3

2012-12-05

Page 51: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions51

Picking small qty

2012-12-05

select s.loc , s.item , least(s.loc_qty, s.ord_qty - s.sum_prv_qty) pick_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Same data as FIFO picking case

But for this case we will use the policy of picking small quantities

Page 52: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions52

Picking small qty

2012-12-05

LOC ITEM PICK_QTY

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

1-A-20 A1 3

1-A-31 A1 12

1-B-11 B1 4

1-B-15 B1 2

1-C-04 B1 11

2-D-07 A1 9

2-D-23 B1 1

Because that gives many picks and shows this case best

Notice anything about these data?

Page 53: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions53

Picking route

• Is this a smart route to drive?

2012-12-05

Page 54: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions54

Better picking route

• We need to change direction every other aisle

2012-12-05

Page 55: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions55

Decipher loc

2012-12-05

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_qtyfrom ( 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 = :pick_order

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

In this case location can be split into warehouse, aisle and position simply by substr()

Page 56: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions56

Decipher loc

2012-12-05

WAREHOUSE AISLE POSITION LOC ITEM PICK_QTY

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

1 A 20 1-A-20 A1 3

1 A 31 1-A-31 A1 12

1 B 11 1-B-11 B1 4

1 B 15 1-B-15 B1 2

1 C 4 1-C-04 B1 11

2 D 7 2-D-07 A1 9

2 D 23 2-D-23 B1 1

Now we can use analytics on the individual parts of the location

Page 57: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions57

Rank aisles

2012-12-05

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_qtyfrom ( 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 = :pick_order) s where s.sum_prv_qty < s.ord_qty order by s.loc

Ordering by warehouse and aisle will give same rank to positions in same aisle

dense_rank() ensures consecutive ranks

Page 58: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions58

Rank aisles

2012-12-05

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY

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

1 A 1 20 1-A-20 A1 3

1 A 1 31 1-A-31 A1 12

1 B 2 11 1-B-11 B1 4

1 B 2 15 1-B-15 B1 2

1 C 3 4 1-C-04 B1 11

2 D 4 7 2-D-07 A1 9

2 D 4 23 2-D-23 B1 1

Now we have numbered the aisles in the order they are to be visited

Page 59: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions59

Odd up, even down

2012-12-05

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 = :pick_order ) 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

mod() and case allows us to order positive on odd aisles and negative on even aisles

Page 60: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions60

Odd up, even down

2012-12-05

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY

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

1 A 1 20 1-A-20 A1 3

1 A 1 31 1-A-31 A1 12

1 B 2 15 1-B-15 B1 2

1 B 2 11 1-B-11 B1 4

1 C 3 4 1-C-04 B1 11

2 D 4 23 2-D-23 B1 1

2 D 4 7 2-D-07 A1 9

And so aisle 1-A is ascending, 1-B is descending,1-C is ascending,2-D is descending

Page 61: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions61

Single door

• Direction has to ”restart” per warehouse

2012-12-05

Page 62: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions62

Partition warehouse

2012-12-05

select s2.warehouse, s2.aisle, s2.aisle_no, s2.position, s2.loc, s2.item, s2.pick_qtyfrom (

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 = :pick_order ) 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

Move the warehouse part from the order by to the partition by

Page 63: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions63

Partition warehouse

2012-12-05

WAREHOUSE AISLE AISLE_NO POSITION LOC ITEM PICK_QTY

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

1 A 1 20 1-A-20 A1 3

1 A 1 31 1-A-31 A1 12

1 B 2 15 1-B-15 B1 2

1 B 2 11 1-B-11 B1 4

1 C 3 4 1-C-04 B1 11

2 D 1 7 2-D-07 A1 9

2 D 1 23 2-D-23 B1 1

Now the aisle_no is restarted for each warehouse, so the first visited aisle of a warehouse is always odd and therefore sorted ascending

Page 64: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions64

Efficient picking route• DENSE_RANK() to number the aisles in order visited

• Order the output– ”Up” on odd aisles– ”Down” on even aisles

• Partition by warehouse if door is missing2012-12-05

Page 65: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions65

Picking efficiencyCase 4

2012-12-05

Page 66: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions66

Picking efficiency• How fast can operators

pick items?• How much do they wait

idle for totes to arrive?

2012-12-05

Page 67: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions67

Table

2012-12-05

create table missions ( missionid number primary key, loadunit number, departpos varchar2(10), departtime date, arrivepos varchar2(10), arrivetime date)/

Missions are everytime a tote (loadunit) goes from one position to another position on the conveyor system

Page 68: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions68

Mission data

2012-12-05

insert into missions values ( 35986751, 10063485, 'STORE', timestamp '2012-04-12 06:38:07', 'PLF4', timestamp '2012-04-12 08:00:03' );

insert into missions values ( 35986752, 10016906, 'STORE', timestamp '2012-04-12 06:38:07', 'PLF4', timestamp '2012-04-12 08:01:41' );

insert into missions values ( 35986754, 10059580, 'STORE', timestamp '2012-04-12 06:38:07', 'PLF4', timestamp '2012-04-12 08:01:09' );

insert into missions values ( 35986755, 10056277, 'STORE', timestamp '2012-04-12 06:38:07', 'PLF4', timestamp '2012-04-12 08:01:16' );

insert into missions values ( 35986757, 10051547, 'STORE', timestamp '2012-04-12 06:38:07', 'PLF4', timestamp '2012-04-12 08:02:40' );

...2690 inserts snipped out...insert into missions values ( 35992214, 10064588, 'PLF4', timestamp '2012-04-12 11:13:20', 'STORE', timestamp '2012-04-12

11:15:12' );insert into missions values ( 35992216, 10066518, 'PLF4', timestamp '2012-04-12 11:13:22', 'STORE', timestamp '2012-04-12

11:15:30' );insert into missions values ( 35992219, 10082114, 'PLF4', timestamp '2012-04-12 11:13:43', 'STORE', timestamp '2012-04-12

11:15:35' );insert into missions values ( 35992220, 10033235, 'PLF4', timestamp '2012-04-12 11:13:52', 'STORE', timestamp '2012-04-12

11:15:50' );insert into missions values ( 35992223, 10056459, 'PLF4', timestamp '2012-04-12 11:14:59', 'STORE', timestamp '2012-04-12

11:21:03' );

Page 69: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions69

Arrivals

2012-12-05

select a.arrivepos pos , a.arrivetime time , a.loadunit , a.missionid from missions a where a.arrivepos in ('PLF4','PLF5') and a.arrivetime >= to_date('2012-04-12 08:00:00',

'YYYY-MM-DD HH24:MI:SS') and a.arrivetime <= to_date('2012-04-12 23:59:59',

'YYYY-MM-DD HH24:MI:SS') order by a.arrivepos, a.arrivetime

All missions arriving at picking stations PLF4 and PLF5 on April 12th after 08:00

Page 70: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions70

Arrivals

2012-12-05

POS TIME LOADUNIT MISSIONID ---- -------- --------- --------- PLF4 08:00:03 10063485 35986751 PLF4 08:00:11 10069588 35986762 PLF4 08:01:09 10059580 35986754 ...PLF4 12:47:51 10069370 35990243 PLF4 12:47:58 10026743 35990248 PLF4 12:49:06 10013439 35990250 PLF5 08:00:00 10040198 35987250 PLF5 08:00:07 10008351 35987251 PLF5 08:00:14 10068629 35987225 ...PLF5 11:28:47 10078376 35990936 PLF5 11:28:56 10035491 35990918 PLF5 11:29:07 10010287 35991015

1453 rows selected.

Page 71: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions71

Departures

2012-12-05

select d.departpos pos , d.departtime time , d.loadunit , d.missionid from missions d where d.departpos in ('PLF4','PLF5') and d.departtime >= to_date('2012-04-12 08:00:00',

'YYYY-MM-DD HH24:MI:SS') and d.departtime <= to_date('2012-04-12 23:59:59',

'YYYY-MM-DD HH24:MI:SS') order by d.departpos, d.departtime

All missions departing from picking stations PLF4 and PLF5 on April 12th after 08:00

Page 72: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions72

Departures

2012-12-05

POS TIME LOADUNIT MISSIONID ---- -------- --------- --------- PLF4 08:00:00 10067235 35988299 PLF4 08:00:08 10063485 35988300 PLF4 08:01:07 10069588 35988307 ...PLF4 11:13:43 10082114 35992219 PLF4 11:13:52 10033235 35992220 PLF4 11:14:59 10056459 35992223 PLF5 08:00:06 10040198 35988296 PLF5 08:00:13 10008351 35988302 PLF5 08:00:35 10068629 35988303 ...PLF5 11:08:36 10018796 35992157 PLF5 11:08:45 10058221 35992158 PLF5 11:09:00 10030575 35992159

1247 rows selected.

Page 73: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions73

Combined events

2012-12-05

select pos, time, ad, loadunit, missionid from ( select a.arrivepos pos, a.arrivetime time, 'A' ad, a.loadunit, a.missionid from missions a where a.arrivepos in ('PLF4','PLF5') and a.arrivetime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and a.arrivetime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS') union all select d.departpos pos, d.departtime time, 'D' ad, d.loadunit, d.missionid from missions d where d.departpos in ('PLF4','PLF5') and d.departtime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and d.departtime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS')) s1order by pos, time

Page 74: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions74

Combined events

2012-12-05

POS TIME AD LOADUNIT MISSIONID ---- -------- -- --------- --------- PLF4 08:00:00 D 10067235 35988299 PLF4 08:00:03 A 10063485 35986751 PLF4 08:00:08 D 10063485 35988300 PLF4 08:00:11 A 10069588 35986762 PLF4 08:01:07 D 10069588 35988307 PLF4 08:01:09 A 10059580 35986754 PLF4 08:01:14 D 10059580 35988308 PLF4 08:01:16 A 10056277 35986755 PLF4 08:01:24 D 10056277 35988309 PLF4 08:01:26 A 10081310 35986764 PLF4 08:01:39 D 10081310 35988310 PLF4 08:01:41 A 10016906 35986752 ...2700 rows selected.

Arrivals and departures joined allows us to see the loadunit arriving and a little bit later departing

Page 75: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions75

Lead the next event

2012-12-05

with s1 as ( select a.arrivepos pos, a.arrivetime time, 'A' ad, a.loadunit, a.missionid from missions a where a.arrivepos in ('PLF4','PLF5') and a.arrivetime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and a.arrivetime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS') union all select d.departpos pos, d.departtime time, 'D' ad, d.loadunit, d.missionid from missions d where d.departpos in ('PLF4','PLF5') and d.departtime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and d.departtime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS')

)select pos , time , lead(time) over ( partition by pos order by time, missionid ) nexttime , ad , loadunit from s1 order by pos, time

The analytic function lead() gives for each row the time of the next row

Page 76: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions76

Lead the next event

2012-12-05

POS TIME NEXTTIME AD LOADUNIT ---- -------- -------- -- --------- PLF4 08:00:00 08:00:03 D 10067235 PLF4 08:00:03 08:00:08 A 10063485 PLF4 08:00:08 08:00:11 D 10063485 PLF4 08:00:11 08:01:07 A 10069588 PLF4 08:01:07 08:01:09 D 10069588 PLF4 08:01:09 08:01:14 A 10059580 PLF4 08:01:14 08:01:16 D 10059580 PLF4 08:01:16 08:01:24 A 10056277 PLF4 08:01:24 08:01:26 D 10056277 PLF4 08:01:26 08:01:39 A 10081310 PLF4 08:01:39 08:01:41 D 10081310 PLF4 08:01:41 08:01:57 A 10016906...2700 rows selected.

So on each ’D’ row NEXTTIME is the time of the following ’A’ row

And on each ’A’ row NEXTTIME is the time of the following ’D’ row

Page 77: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions77

Lead on

2012-12-05

with s1 as ( select a.arrivepos pos, a.arrivetime time, 'A' ad, a.loadunit, a.missionid from missions a where a.arrivepos in ('PLF4','PLF5') and a.arrivetime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and a.arrivetime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS') union all select d.departpos pos, d.departtime time, 'D' ad, d.loadunit, d.missionid from missions d where d.departpos in ('PLF4','PLF5') and d.departtime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and d.departtime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS')

)select pos, time , lead(time) over ( partition by pos order by time, missionid ) nexttime , lead(time,2) over ( partition by pos order by time, missionid ) next2time , ad, loadunit from s1 order by pos, time

lead() accepts a second parameter telling how many rows forward the function should ”look”

Page 78: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions78

Lead on

2012-12-05

POS TIME NEXTTIME NEXT2TIM AD LOADUNIT ---- -------- -------- -------- -- --------- PLF4 08:00:00 08:00:03 08:00:08 D 10067235 PLF4 08:00:03 08:00:08 08:00:11 A 10063485 PLF4 08:00:08 08:00:11 08:01:07 D 10063485 PLF4 08:00:11 08:01:07 08:01:09 A 10069588 PLF4 08:01:07 08:01:09 08:01:14 D 10069588 PLF4 08:01:09 08:01:14 08:01:16 A 10059580 PLF4 08:01:14 08:01:16 08:01:24 D 10059580 PLF4 08:01:16 08:01:24 08:01:26 A 10056277 PLF4 08:01:24 08:01:26 08:01:39 D 10056277 PLF4 08:01:26 08:01:39 08:01:41 A 10081310 PLF4 08:01:39 08:01:41 08:01:57 D 10081310 PLF4 08:01:41 08:01:57 08:01:59 A 10016906...2700 rows selected.

The NEXT2TIME column ”looks” 2 rows forward

Page 79: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions79

Filter double lead

2012-12-05

with s1 as ( select a.arrivepos pos, a.arrivetime time, 'A' ad, a.loadunit, a.missionid from missions a where a.arrivepos in ('PLF4','PLF5') and a.arrivetime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and a.arrivetime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS') union all select d.departpos pos, d.departtime time, 'D' ad, d.loadunit, d.missionid from missions d where d.departpos in ('PLF4','PLF5') and d.departtime >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and d.departtime <= to_date('2012-04-12 23:59:59','YYYY-MM-DD HH24:MI:SS'))

select pos, time arrive, nexttime depart, next2time nextarrive, loadunit from ( select pos, time , lead(time) over ( partition by pos order by time, missionid ) nexttime , lead(time,2) over ( partition by pos order by time, missionid ) next2time , ad, loadunit from s1 ) s2 where ad = 'A' order by pos, arrive

Since we use the double lead we now have all the data necessary on the ’A’ rows and do not need the ’D’ rows anymore

Page 80: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions80

Filter double lead

2012-12-05

POS ARRIVE DEPART NEXTARRI LOADUNIT ---- -------- -------- -------- --------- PLF4 08:00:03 08:00:08 08:00:11 10063485 PLF4 08:00:11 08:01:07 08:01:09 10069588 PLF4 08:01:09 08:01:14 08:01:16 10059580 PLF4 08:01:16 08:01:24 08:01:26 10056277 PLF4 08:01:26 08:01:39 08:01:41 10081310 PLF4 08:01:41 08:01:57 08:01:59 10016906...PLF4 10:59:47 10:59:54 10:59:56 10076144 PLF4 10:59:56 11:00:11 11:00:12 10012882 PLF4 11:00:12 11:00:28 11:00:29 10035898 PLF4 11:00:29 11:00:42 11:00:44 10076793...1453 rows selected.

We can now see a tote arrives 08:00:03, leaves again at 08:00:08, and a new tote arrives at 08:00:11

Note the tote that arrived 10:59:56 leaves after 11:00:00

Page 81: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions81

Pick and wait

2012-12-05

with s1 as ( ... )

select pos, arrive, depart, nextarrive , (depart - arrive) * 24 * 60 * 60 pickseconds , (nextarrive - depart) * 24 * 60 * 60 waitseconds from ( select pos, time arrive, nexttime depart, next2time nextarrive, loadunit from ( select pos, time , lead(time) over ( partition by pos order by time, missionid ) nexttime , lead(time,2) over ( partition by pos order by time, missionid ) next2time , ad, loadunit from s1 ) s2 where ad = 'A'

) s3where arrive >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and arrive <= to_date('2012-04-12 10:59:59','YYYY-MM-DD HH24:MI:SS')order by pos, arrive

Calculate pick seconds and wait seconds

Filter on desired 3 hour interval

Page 82: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions82

Pick and wait

2012-12-05

POS ARRIVE DEPART NEXTARRI PICKSECONDS WAITSECONDS ---- -------- -------- -------- ----------- ----------- PLF4 08:00:03 08:00:08 08:00:11 5 3 PLF4 08:00:11 08:01:07 08:01:09 56 2 PLF4 08:01:09 08:01:14 08:01:16 5 2 PLF4 08:01:16 08:01:24 08:01:26 8 2...PLF4 08:58:08 08:58:08 09:11:36 0 808 PLF4 09:11:36 09:12:55 09:12:56 79 1 ...PLF4 10:59:47 10:59:54 10:59:56 7 2 PLF4 10:59:56 11:00:11 11:00:12 15 1 PLF5 08:00:00 08:00:06 08:00:07 6 1 PLF5 08:00:07 08:00:13 08:00:14 6 1...PLF5 10:57:54 10:59:58 10:59:59 124 1 PLF5 10:59:59 11:00:09 11:00:10 10 1

1155 rows selected.

How fast did the operator pick and how long time did he wait for a new tote to arrive

Page 83: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions83

Hourly stats

2012-12-05

with s1 as ( ... )

select pos , trunc(arrive,'HH24') hour , count(*) picks , avg(pickseconds) secondsprpick , sum(pickseconds)/60 minutespicked , 100*sum(pickseconds)/sum(pickseconds+waitseconds) pickpct , avg(waitseconds) secondsprwait , sum(waitseconds)/60 minuteswaited , 100*sum(waitseconds)/sum(pickseconds+waitseconds) waitpct , avg(pickseconds+waitseconds) secondsprcycle , sum(pickseconds+waitseconds)/60 minutestotal , 60 * count(*) / sum(pickseconds+waitseconds) cyclesprmin from ( select pos, arrive, depart, nextarrive , (depart - arrive) * 24 * 60 * 60 pickseconds , (nextarrive - depart) * 24 * 60 * 60 waitseconds from ( select pos, time arrive, nexttime depart, next2time nextarrive, loadunit from ( select pos, time , lead(time) over ( partition by pos order by time, missionid ) nexttime , lead(time,2) over ( partition by pos order by time, missionid ) next2time , ad, loadunit from s1 ) s2 where ad = 'A' ) s3 where arrive >= to_date('2012-04-12 08:00:00','YYYY-MM-DD HH24:MI:SS') and arrive <= to_date('2012-04-12 10:59:59','YYYY-MM-DD HH24:MI:SS')

) s4group by pos, trunc(arrive,'HH24')order by pos, trunc(arrive,'HH24')

Now we can use the previous select as basis for some plain statistics by the hour

Page 84: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions84

Hourly stats

2012-12-05

sec sec sec cycle

pr min pick pr min wait pr min pr

POS HOUR PICKS pick pickd pct wait waitd pct cycle total min

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

PLF4 08:00:00 156 20.3 52.9 73.9 7.2 18.7 26.1 27.5 71.6 2.2

PLF4 09:00:00 159 13.2 35.0 71.9 5.1 13.6 28.1 18.3 48.6 3.3

PLF4 10:00:00 165 19.8 54.5 90.8 2.0 5.5 9.2 21.8 60.0 2.8

PLF5 08:00:00 247 12.9 53.2 85.3 2.2 9.2 14.7 15.2 62.4 4.0

PLF5 09:00:00 179 15.9 47.4 82.3 3.4 10.2 17.7 19.3 57.6 3.1

PLF5 10:00:00 249 10.9 45.4 75.3 3.6 14.9 24.7 14.5 60.4 4.1

6 rows selected.

Page 85: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions85

Picking efficiency• Log over tote missions arriving and departing the picking

stations

• LEAD() on mission log to find the departure following an arrival => picking time

• LEAD(,2) on mission log to find the arrival following a departure => waiting time

2012-12-05

Page 86: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions86

Forecasting salesCase 5

2012-12-05

Page 87: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions87

Forecasting sales• Forecast the sales

of next year• But follow the

trend of the item

2012-12-05

Page 88: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions88

Table

2012-12-05

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

Simple table of monthly sales by item

Page 89: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions89

Data

2012-12-05

insert into sales values ('Snowchain', date '2008-01-01', 79); insert into sales values ('Snowchain', date '2008-02-01', 133); insert into sales values ('Snowchain', date '2008-03-01', 24); ... insert into sales values ('Snowchain', date '2010-10-01', 1); insert into sales values ('Snowchain', date '2010-11-01', 73); insert into sales values ('Snowchain', date '2010-12-01', 160); insert into sales values ('Sunshade' , date '2008-01-01', 4); insert into sales values ('Sunshade' , date '2008-02-01', 6); insert into sales values ('Sunshade' , date '2008-03-01', 32); ... insert into sales values ('Sunshade' , date '2010-10-01', 11); insert into sales values ('Sunshade' , date '2010-11-01', 3); insert into sales values ('Sunshade' , date '2010-12-01', 5);

Item Snowchain sells good in winter and trends up

Item Sunshade sells good in summer and trends down

Page 90: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions90

Slope

2012-12-05

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

Graph slope: y-axis is qty x-axis is a number with the scale of 1=a monthRange between gives sliding 2-year window

Page 91: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions91

Slope

2012-12-05

ITEM MTH QTY SLOPE ---------- ---------- ----- -------- Snowchain 2008-01-01 79 Snowchain 2008-02-01 133 54.000 Snowchain 2008-03-01 24 -27.500 ...Snowchain 2010-10-01 1 -2.274 Snowchain 2010-11-01 73 -2.363 Snowchain 2010-12-01 160 -.991 Sunshade 2008-01-01 4 Sunshade 2008-02-01 6 2.000 Sunshade 2008-03-01 32 14.000 ...Sunshade 2010-10-01 11 .217 Sunshade 2010-11-01 3 -.200 Sunshade 2010-12-01 5 -.574

72 rows selected.

Slope value most accurate for 2010 data where 2 year sliding window contains full set of data

Page 92: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions92

Transpose using slope

2012-12-05

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 '2010-01-01' order by item, mth

As x-axis had scale of 1=a month and y-axis was qty, multiplying slope with 12 gives how much qty goes up or down in a year

Page 93: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions93

Transpose using slope

2012-12-05

ITEM MTH QTY QTY_NEXT_YEAR

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

Snowchain 2010-01-01 167 188,313043

Snowchain 2010-02-01 247 304,855652

Snowchain 2010-03-01 42 96,3913043

Snowchain 2010-04-01 0 42,6991304

Snowchain 2010-05-01 0 30,8869565

Snowchain 2010-06-01 0 19,0747826

Snowchain 2010-07-01 0 7,2626087

Snowchain 2010-08-01 1 -3,4295652

Snowchain 2010-09-01 0 -16,121739

Snowchain 2010-10-01 1 -26,292174

Snowchain 2010-11-01 73 44,6434783

Snowchain 2010-12-01 160 148,109565

Sunshade 2010-01-01 2 -11,617391

Sunshade 2010-02-01 8 -11,137391

Sunshade 2010-03-01 28 9,11304348

Sunshade 2010-04-01 26 8,86086957

Sunshade 2010-05-01 23 9,66434783

Sunshade 2010-06-01 46 39,1130435

Sunshade 2010-07-01 73 79,4486957

Sunshade 2010-08-01 25 31,7147826

Sunshade 2010-09-01 13 18,0504348

Sunshade 2010-10-01 11 13,6086957

Sunshade 2010-11-01 3 ,594782609

Sunshade 2010-12-01 5 -1,8869565

Page 94: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions94

Forecast

2012-12-05

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 '2010-01-01' order by item, mth

Rather than column QTY_NEXT_YEAR we add a year to the month and call it a forecast

We round the numbers and skip any negatives

Page 95: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions95

Forecast

2012-12-05

ITEM MTH FORECAST

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

Snowchain 2011-01-01 188

Snowchain 2011-02-01 305

Snowchain 2011-03-01 96

Snowchain 2011-04-01 43

Snowchain 2011-05-01 31

Snowchain 2011-06-01 19

Snowchain 2011-07-01 7

Snowchain 2011-08-01 0

Snowchain 2011-09-01 0

Snowchain 2011-10-01 0

Snowchain 2011-11-01 45

Snowchain 2011-12-01 148

Sunshade 2011-01-01 0

Sunshade 2011-02-01 0

Sunshade 2011-03-01 9

Sunshade 2011-04-01 9

Sunshade 2011-05-01 10

Sunshade 2011-06-01 39

Sunshade 2011-07-01 79

Sunshade 2011-08-01 32

Sunshade 2011-09-01 18

Sunshade 2011-10-01 14

Sunshade 2011-11-01 1

Sunshade 2011-12-01 0

Page 96: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions96

Actual + forecast

2012-12-05

select item, mth, qty, type 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 '2010-01-01' ) order by item, mth

UNION ALL of the actual data and the forecast data for a complete set of sales data that can be shown in a graph

Page 97: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions97

Actual + forecast

2012-12-05

ITEM MTH QTY TYPE

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

Snowchain 2008-01-01 79 Actual

Snowchain 2008-02-01 133 Actual

Snowchain 2008-03-01 24 Actual ...Snowchain 2010-10-01 1 Actual

Snowchain 2010-11-01 73 Actual

Snowchain 2010-12-01 160 Actual

Snowchain 2011-01-01 188 Forecast

Snowchain 2011-02-01 305 Forecast

Snowchain 2011-03-01 96 Forecast ...Snowchain 2011-10-01 0 Forecast

Snowchain 2011-11-01 45 Forecast

Snowchain 2011-12-01 148 Forecast

Sunshade 2008-01-01 4 Actual

Sunshade 2008-02-01 6 Actual

Sunshade 2008-03-01 32 Actual ...Sunshade 2010-10-01 11 Actual

Sunshade 2010-11-01 3 Actual

Sunshade 2010-12-01 5 Actual

Sunshade 2011-01-01 0 Forecast

Sunshade 2011-02-01 0 Forecast

Sunshade 2011-03-01 9 Forecast ...Sunshade 2011-10-01 14 Forecast

Sunshade 2011-11-01 1 Forecast

Sunshade 2011-12-01 0 Forecast

Page 98: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions98

Actual + forecast

Data from previous slide is the graph

• ”Actual” is normal lines

• ”Forecast” is stapled lines

2012-12-05

Page 99: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions99

Forecasting sales• REGR_SLOPE() to calculate trend

• RANGE window for sliding trend calculation over three years

• ”Transpose” last years sales by the slope to get next years forecast

2012-12-05

Page 100: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions100

Forecast zero stockCase 6

2012-12-05

Page 101: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions101

Forecast zero stock• Fireworks sell like crazy

last week of December• What hour will a store

run out of stock?

2012-12-05

Page 102: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions102

Tables

2012-12-05

create table fw_store ( shopid varchar2(10) primary key, containers integer)/

create table fw_sales ( shopid varchar2(10) references fw_store (shopid), saleshour date, salesnem number)/

Stores are defined by how many storage containers

Sales are hourly data per shop inNet Explosive Mass

Page 103: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions103

Tables

2012-12-05

create table fw_daybudget ( shopid varchar2(10) references fw_store (shopid), budgetdate date, budgetnem number)/

create table fw_hourbudget ( hour integer, percent number)/

Daily budget ofNet Explosive Massper shop

Percentage of a days budget expected to be in each hour

Page 104: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions104

Data - store

2012-12-05

insert into fw_store values ('AALBORG' , 4); insert into fw_store values ('GLOSTRUP' , 4); insert into fw_store values ('HADERSLEV', 3);

Page 105: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions105

Data - sales

2012-12-05

insert into fw_salesselect shopid, day + numtodsinterval(hour,'hour') saleshour, salesnemfrom ( select 'AALBORG' shopid, date '2011-12-27' day, 4 h9, 6 h10, 5 h11, 20 h12, 19 h13, 22 h14, 27 h15, 11 h16, 16 h17, 4 h18 from dual union all select 'AALBORG' , date '2011-12-28', 7, 17, 18, 13, 27, 28, 20, 14, 10, 19 from dual union all select 'AALBORG' , date '2011-12-29', 10, 14, 20, null, null, null, null, null, null, null from dual union all select 'GLOSTRUP' , date '2011-12-27', 1, 6, 6, 14, 17, 17, 13, 15, 7, 7 from dual union all select 'GLOSTRUP' , date '2011-12-28', 4, 14, 30, 35, 22, 21, 35, 34, 15, 25 from dual union all select 'GLOSTRUP' , date '2011-12-29', 6, 13, 50, null, null, null, null, null, null, null from dual union all select 'HADERSLEV', date '2011-12-27', 4, 7, 13, 15, 17, 13, 18, 19, 10, 3 from dual union all select 'HADERSLEV', date '2011-12-28', 8, 5, 14, 18, 20, 18, 15, 24, 12, 1 from dual union all select 'HADERSLEV', date '2011-12-29', 1, 19, 33, null, null, null, null, null, null, null from dual) s1unpivot exclude nulls ( salesnem for hour in ( h9 as 9, h10 as 10, h11 as 11, h12 as 12, h13 as 13, h14 as 14, h15 as 15, h16 as 16, h17 as 17, h18 as 18 ))

Page 106: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions106

Data - daybudget

2012-12-05

insert into fw_daybudget values ('AALBORG' , date '2011-12-27', 150); insert into fw_daybudget values ('AALBORG' , date '2011-12-28', 200); insert into fw_daybudget values ('AALBORG' , date '2011-12-29', 300); insert into fw_daybudget values ('AALBORG' , date '2011-12-30', 500); insert into fw_daybudget values ('AALBORG' , date '2011-12-31', 400); insert into fw_daybudget values ('GLOSTRUP' , date '2011-12-27', 150); insert into fw_daybudget values ('GLOSTRUP' , date '2011-12-28', 200); insert into fw_daybudget values ('GLOSTRUP' , date '2011-12-29', 300); insert into fw_daybudget values ('GLOSTRUP' , date '2011-12-30', 500); insert into fw_daybudget values ('GLOSTRUP' , date '2011-12-31', 400); insert into fw_daybudget values ('HADERSLEV', date '2011-12-27', 100); insert into fw_daybudget values ('HADERSLEV', date '2011-12-28', 150); insert into fw_daybudget values ('HADERSLEV', date '2011-12-29', 200); insert into fw_daybudget values ('HADERSLEV', date '2011-12-30', 400); insert into fw_daybudget values ('HADERSLEV', date '2011-12-31', 300);

Page 107: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions107

Data - hourbudget

2012-12-05

insert into fw_hourbudget values ( 9, 4); insert into fw_hourbudget values (10, 8); insert into fw_hourbudget values (11, 10); insert into fw_hourbudget values (12, 12); insert into fw_hourbudget values (13, 12); insert into fw_hourbudget values (14, 12); insert into fw_hourbudget values (15, 14); insert into fw_hourbudget values (16, 14); insert into fw_hourbudget values (17, 10); insert into fw_hourbudget values (18, 4);

Page 108: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions108

Starting NEM

2012-12-05

select s.shopid , s.containers * 250 startnem from fw_store sorder by s.shopid

start

SHOPID nem

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

AALBORG 1000

GLOSTRUP 1000

HADERSLEV 750

Three stores:

2 has 4 containers( = 1000 kg NEM)

1 has 3 containers( = 750 kg NEM)

Page 109: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions109

Budget per hour

2012-12-05

select db.shopid , db.budgetdate + numtodsinterval(hb.hour,'hour')

budgethour , db.budgetnem * hb.percent / 100 budgetnem from fw_daybudget db cross join fw_hourbudget hborder by db.shopid, budgethour

Cartesian join of daily budget with hour percentages gives us an hourly budget

Page 110: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions110

Budget per hour

2012-12-05

budgt SHOPID BUDGETHOUR nem ---------- ------------------- ------ AALBORG 2011-12-27 09:00:00 6 AALBORG 2011-12-27 10:00:00 12 AALBORG 2011-12-27 11:00:00 15 ...AALBORG 2011-12-31 15:00:00 56 AALBORG 2011-12-31 16:00:00 56 AALBORG 2011-12-31 17:00:00 40 AALBORG 2011-12-31 18:00:00 16 GLOSTRUP 2011-12-27 09:00:00 6 GLOSTRUP 2011-12-27 10:00:00 12 GLOSTRUP 2011-12-27 11:00:00 15 ...HADERSLEV 2011-12-31 16:00:00 42 HADERSLEV 2011-12-31 17:00:00 30 HADERSLEV 2011-12-31 18:00:00 12

150 rows selected.

These hourly budget data is now directly comparable to hourly sales data

Page 111: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions111

WITH clauses

2012-12-05

with shop as ( select s.shopid , s.containers * 250 startnem from fw_store s), budget as ( select db.shopid , db.budgetdate + numtodsinterval(hb.hour,'hour')

budgethour , db.budgetnem * hb.percent / 100 budgetnem from fw_daybudget db cross join fw_hourbudget hb)...

Use the starting NEM and hourly budget selects as WITH clauses

Page 112: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions112

Budget + sales

2012-12-05

... Shop and Budget WITH clauses ...select budget.shopid, shop.startnem, budget.budgethour hour , budget.budgetnem , sum(budget.budgetnem) over ( partition by budget.shopid order by budget.budgethour rows between unbounded preceding and current row ) budgetnemacc , sales.salesnem , sum(sales.salesnem) over ( partition by budget.shopid order by budget.budgethour rows between unbounded preceding and current row ) salesnemacc from shop join budget on budget.shopid = shop.shopid left outer join fw_sales sales on sales.shopid = budget.shopid and sales.saleshour = budget.budgethourorder by budget.shopid, budget.budgethour

Join shop and budget, outer join to sales – then we can accumulate both budget and sales

Page 113: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions113

Budget + sales

2012-12-05

budgt sales start budgt nem sales nem SHOPID nem HOUR nem acc nem acc ---------- ------ ------------------- ------ ------ ------ ------ AALBORG 1000 2011-12-27 09:00:00 6 6 4 4 AALBORG 1000 2011-12-27 10:00:00 12 18 6 10 AALBORG 1000 2011-12-27 11:00:00 15 33 5 15 AALBORG 1000 2011-12-27 12:00:00 18 51 20 35 ...AALBORG 1000 2011-12-29 10:00:00 24 386 14 331 AALBORG 1000 2011-12-29 11:00:00 30 416 20 351 AALBORG 1000 2011-12-29 12:00:00 36 452 351 AALBORG 1000 2011-12-29 13:00:00 36 488 351 ...AALBORG 1000 2011-12-31 15:00:00 56 1438 351 AALBORG 1000 2011-12-31 16:00:00 56 1494 351 AALBORG 1000 2011-12-31 17:00:00 40 1534 351 AALBORG 1000 2011-12-31 18:00:00 16 1550 351

”Now” is December 29th exactly 12:00, so sales data stops thereAccumulated data show we are behind budget

Page 114: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions114

Yet another WITH

2012-12-05

... Shop and Budget WITH clauses ...

), nem as ( select budget.shopid, shop.startnem, budget.budgethour hour , case when budget.budgethour < to_date('2011-12-29 12:00:00', 'YYYY-MM-DD HH24:MI:SS') then 'S' else 'B' end salesbudget , case when budget.budgethour < to_date('2011-12-29 12:00:00', 'YYYY-MM-DD HH24:MI:SS') then nvl(sales.salesnem,0) else budget.budgetnem end qtynem from shop join budget on budget.shopid = shop.shopid left outer join fw_sales sales on sales.shopid = budget.shopid and sales.saleshour = budget.budgethour)

Real code use SYSDATE rather than hardcodet

qtynem contains actual sales for as long as we have it, and budget data after ”now”

Page 115: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions115

Stock level

2012-12-05

... Shop, Budget and Nem WITH clauses ...select nem.shopid , nem.hour , nem.salesbudget , nem.qtynem , sum(nem.qtynem) over ( partition by nem.shopid order by nem.hour rows between unbounded preceding and current row ) sumnem , greatest(nem.startnem - nvl( sum(nem.qtynem) over ( partition by nem.shopid order by nem.hour rows between unbounded preceding and 1 preceding ) ,0),0) stocknem from nem order by shopid, hour

Accumulate qtynem similar to FIFO code and subtract from startnem to calculate stock at the beginning of each hour

Page 116: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions116

Stock level

2012-12-05

S qty sum stock SHOPID HOUR B nem nem nem ---------- ------------------- - ------ ------ ------ AALBORG 2011-12-27 09:00:00 S 4 4 1000 AALBORG 2011-12-27 10:00:00 S 6 10 996 AALBORG 2011-12-27 11:00:00 S 5 15 990 ...AALBORG 2011-12-29 10:00:00 S 14 331 683 AALBORG 2011-12-29 11:00:00 S 20 351 669 AALBORG 2011-12-29 12:00:00 B 36 387 649 AALBORG 2011-12-29 13:00:00 B 36 423 613 ...AALBORG 2011-12-30 15:00:00 B 70 945 125 AALBORG 2011-12-30 16:00:00 B 70 1015 55 AALBORG 2011-12-30 17:00:00 B 50 1065 0 AALBORG 2011-12-30 18:00:00 B 20 1085 0 ...AALBORG 2011-12-31 17:00:00 B 40 1469 0 AALBORG 2011-12-31 18:00:00 B 16 1485 0

At the beginning of hour 16 on December 30th, there will be 55 kg NEM leftDuring the hour we expect to sell 70 kg and will run out

Page 117: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions117

The hour of zero stock

2012-12-05

... Shop, Budget and Nem WITH clauses ...

select shopid , max(hour) + numtodsinterval( max(stocknem) keep (dense_rank last order by hour) / max(qtynem) keep (dense_rank last order by hour) ,'hour' ) zerohour from ( select nem.shopid, nem.hour, nem.salesbudget, nem.qtynem , sum(nem.qtynem) over ( partition by nem.shopid order by nem.hour rows between unbounded preceding and current row ) sumnem , greatest(nem.startnem - nvl( sum(nem.qtynem) over ( partition by nem.shopid order by nem.hour rows between unbounded preceding and 1 preceding ) ,0),0) stocknem from nem

) where stocknem > 0 group by shopid order by shopid

The last hour we still have stockThe stock we have left divided by the qty expected sold that hour gives the last fraction of hour before we reach zero

Page 118: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions118

The hour of zero stock

2012-12-05

SHOPID ZEROHOUR

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

AALBORG 2011-12-30 16:47:08

GLOSTRUP 2011-12-30 15:59:08

HADERSLEV 2011-12-30 15:58:55

And so our logistics planner has a forecast for when the shops will run out of fireworks

Page 119: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions119

Model the same

2012-12-05

... Shop, Budget and Nem WITH clauses ...select shopid, rn, hour, startnem, salesbudget, qtynem, sumnem, stocknem, zerohourfrom nemmodelpartition by (shopid)dimension by (rn)measures ( hour, startnem, salesbudget, qtynem, qtynem sumnem, startnem stocknem, cast(null as date) zerohour)rules sequential order iterate (49) ( sumnem[iteration_number+1] = sumnem[iteration_number] + qtynem[iteration_number], stocknem[iteration_number+1] = stocknem[iteration_number] - qtynem[iteration_number] + case when qtynem[iteration_number] > stocknem[iteration_number] then startnem[0] else 0 end, zerohour[iteration_number+1] = case when qtynem[iteration_number+1]>stocknem[iteration_number+1] then hour[iteration_number+1] + numtodsinterval(stocknem[iteration_number+1] / qtynem[iteration_number+1], 'hour’) else null end)order by shopid, hour

Page 120: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions120

Model the same

2012-12-05

start S qty sum stock SHOPID RN HOUR nem B nem nem nem ZEROHOUR ---------- --- ------------------- ------ - ------ ------ ------ ------------------- AALBORG 0 2011-12-27 09:00:00 1000 S 4 4 1000 AALBORG 1 2011-12-27 10:00:00 1000 S 6 8 996 AALBORG 2 2011-12-27 11:00:00 1000 S 5 14 990 ...AALBORG 35 2011-12-30 14:00:00 1000 B 60 819 185 AALBORG 36 2011-12-30 15:00:00 1000 B 70 879 125 AALBORG 37 2011-12-30 16:00:00 1000 B 70 949 55 2011-12-30 16:47:08 AALBORG 38 2011-12-30 17:00:00 1000 B 50 1019 985 AALBORG 39 2011-12-30 18:00:00 1000 B 20 1069 935 ...AALBORG 48 2011-12-31 17:00:00 1000 B 40 1433 571 AALBORG 49 2011-12-31 18:00:00 1000 B 16 1473 531

Page 121: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions121

Change the law

2012-12-05

with shop as ( select s.shopid , s.containers * 100 startnem from fw_store s), budget as (...), nem as (...)select shopid, rn, hour, startnem, salesbudget, qtynem, sumnem, stocknem, zerohourfrom nemmodel...

If politicians decide no longer 250 kg NEM per container, now 100 kg NEM per container

Page 122: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions122

Change the law

2012-12-05

start S qty sum stock SHOPID RN HOUR nem B nem nem nem ZEROHOUR ---------- --- ------------------- ------ - ------ ------ ------ ------------------- AALBORG 0 2011-12-27 09:00:00 400 S 4 4 400 AALBORG 1 2011-12-27 10:00:00 400 S 6 8 396 ...AALBORG 23 2011-12-29 12:00:00 400 B 36 355 49 AALBORG 24 2011-12-29 13:00:00 400 B 36 391 13 2011-12-29 13:21:40 AALBORG 25 2011-12-29 14:00:00 400 B 36 427 377 ...AALBORG 33 2011-12-30 12:00:00 400 B 60 699 105 AALBORG 34 2011-12-30 13:00:00 400 B 60 759 45 2011-12-30 13:45:00 AALBORG 35 2011-12-30 14:00:00 400 B 60 819 385 ...AALBORG 42 2011-12-31 11:00:00 400 B 40 1137 67 AALBORG 43 2011-12-31 12:00:00 400 B 48 1177 27 2011-12-31 12:33:45 AALBORG 44 2011-12-31 13:00:00 400 B 48 1225 379 ...AALBORG 49 2011-12-31 18:00:00 400 B 16 1473 131

Using MODEL clause allows for forecast repeated refill of stock

Page 123: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions123

Zero hours

2012-12-05

... Shop, Budget and Nem WITH clauses ...select shopid, zerohourfrom ( select shopid, rn, hour, startnem, salesbudget, qtynem, sumnem, stocknem, zerohour from nem model partition by (shopid) dimension by (rn) measures ( hour, startnem, salesbudget, qtynem, qtynem sumnem, startnem stocknem, cast(null as date) zerohour ) rules sequential order iterate (49) ( sumnem[iteration_number+1] = sumnem[iteration_number] + qtynem[iteration_number], stocknem[iteration_number+1] = stocknem[iteration_number] - qtynem[iteration_number] + case when qtynem[iteration_number] > stocknem[iteration_number] then startnem[0] else 0 end, zerohour[iteration_number+1] = case when qtynem[iteration_number+1]>stocknem[iteration_number+1] then hour[iteration_number+1] + numtodsinterval(stocknem[iteration_number+1] / qtynem[iteration_number+1], 'hour’) else null end )

)where zerohour is not nullorder by shopid, zerohour

Page 124: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions124

Zero hours

2012-12-05

SHOPID ZEROHOUR

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

AALBORG 2011-12-29 13:21:40

AALBORG 2011-12-30 13:45:00

AALBORG 2011-12-31 12:33:45

GLOSTRUP 2011-12-29 11:51:36

GLOSTRUP 2011-12-30 12:49:00

GLOSTRUP 2011-12-31 11:16:30

HADERSLEV 2011-12-29 11:47:16

HADERSLEV 2011-12-30 13:01:15

HADERSLEV 2011-12-31 11:02:00

With smaller amount in the containers we need to refill shops multiple times

Page 125: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions125

Forecast zero stock• SUM() on budget sales data from ”now” forward

• Identify hour when rolling sum exceeds stock– BETWEEN CURRENT ROW AND 1 PRECEDING

(Similar technique as picking by FIFO)

• More than analytics:– MODEL clause for repeated refill of stock2012-12-05

Page 126: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions126

Multi-order FIFO pickingCase 7

2012-12-05

Page 127: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions127

Multi-order FIFOMonty Latiolais, president of ODTUG:

• Need to pick multiple orders• Each order by First-In-First-Out• Second order ”continues” where

first order stops and so on

2012-12-05

Page 128: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions128

Tables

2012-12-05

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)/

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

Just like Case 2

Page 129: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions129

Data

2012-12-05

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

Inventory data exactly like Case 2Orderline data this time for three orders:

Page 130: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions130

Batch pick

2012-12-05

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 <FIFO sql>...

Group by on orderline creates an orderbatch

Page 131: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions131

Batch pick

2012-12-05

with orderbatch as ( ...)

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

We can apply the FIFO code on the orderbatch

Page 132: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions132

Batch pick

2012-12-05

LOC ITEM PICK_QTY

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

1-A-02 B1 5

1-A-20 A1 18

1-B-11 B1 4

1-B-15 B1 2

1-C-04 B1 12

1-C-05 A1 6

2-A-02 A1 24

2-D-23 B1 1

Works OK, but operator cannot see how much of each pick goes to what order

Page 133: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions133

Pick qty intervals

2012-12-05

with orderbatch as ( ... )

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

Let’s use more analytics

Two rolling sums allow us to calculate from_qty and to_qty

Page 134: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions134

Pick qty intervals

2012-12-05

LOC ITEM PICK_QTY FROM_QTY TO_QTY

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

1-A-20 A1 18 1 18

2-A-02 A1 24 19 42

1-C-05 A1 6 43 48

1-B-15 B1 2 1 2

1-C-04 B1 12 3 14

2-D-23 B1 1 15 15

1-B-11 B1 4 16 19

1-A-02 B1 5 20 24

So ”from 1 to 18” of the 48 pieces of A1 are picked in the first location”From 19 to 42” are picked in the second locationAnd so on…

Page 135: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions135

Order qty intervals

2012-12-05

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

Do the same with the order quantities

Page 136: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions136

Order qty intervals

2012-12-05

ORDNO ITEM QTY FROM_QTY TO_QTY

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

51 A1 24 1 24

62 A1 8 25 32

73 A1 16 33 48

51 B1 18 1 18

73 B1 6 19 24

”From 1 to 24” of the 48 pieces ordered of A1 is on order no 51And so on

Page 137: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions137

Overlapping intervals

2012-12-05

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 (...

The orderlines with qty intervals we put in a with clause

Page 138: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions138

Overlapping intervals

2012-12-05

...), orderbatch as ( select o.item , sum(o.qty) qty from orderlines o group by o.item), fifo as (...

We create the orderbatch from the orderlines as a second with clause

Page 139: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions139

Overlapping intervals

2012-12-05

...

), 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)...

And the FIFO calculation with qty intervals as a third with clause

Page 140: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions140

Overlapping intervals

2012-12-05

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 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

Now we join the fifo and orderlines on overlapping intervals

Page 141: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions141

Overlapping intervals

2012-12-05

LOC ITEM PURCH PICK_QTY FROM_QTY TO_QTY ORDNO QTY FROM_QTY TO_QTY ------ ---- ---------- -------- -------- ------ ----- ------ -------- ------ 1-A-20 A1 2004-11-01 18 1 18 51 24 1 24 2-A-02 A1 2004-11-02 24 19 42 51 24 1 24 2-A-02 A1 2004-11-02 24 19 42 62 8 25 32 2-A-02 A1 2004-11-02 24 19 42 73 16 33 48 1-C-05 A1 2004-11-03 6 43 48 73 16 33 48 1-B-15 B1 2004-11-02 2 1 2 51 18 1 18 1-C-04 B1 2004-11-03 12 3 14 51 18 1 18 2-D-23 B1 2004-11-04 1 15 15 51 18 1 18 1-B-11 B1 2004-11-05 4 16 19 51 18 1 18 1-B-11 B1 2004-11-05 4 16 19 73 6 19 24 1-A-02 B1 2004-11-06 5 20 24 73 6 19 24

The single pick of 24 at location 2-A-02 is joined to three orderlines all with overlapping intervals

Page 142: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions142

Individual pick qty

2012-12-05

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

The intervals can now be used for calculating how much from the location is picked for the individual order

Page 143: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions143

Individual pick qty

2012-12-05

LOC ITEM PURCH PICK_QTY FROM_QTY TO_QTY ORDNO QTY FROM_QTY TO_QTY PICK_ORD_QTY

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

1-A-20 A1 2004-11-01 18 1 18 51 24 1 24 18

2-A-02 A1 2004-11-02 24 19 42 51 24 1 24 6

2-A-02 A1 2004-11-02 24 19 42 62 8 25 32 8

2-A-02 A1 2004-11-02 24 19 42 73 16 33 48 10

1-C-05 A1 2004-11-03 6 43 48 73 16 33 48 6

1-B-15 B1 2004-11-02 2 1 2 51 18 1 18 2

1-C-04 B1 2004-11-03 12 3 14 51 18 1 18 12

2-D-23 B1 2004-11-04 1 15 15 51 18 1 18 1

1-B-11 B1 2004-11-05 4 16 19 51 18 1 18 3

1-B-11 B1 2004-11-05 4 16 19 73 6 19 24 1

1-A-02 B1 2004-11-06 5 20 24 73 6 19 24 5

Page 144: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions144

Pick list

2012-12-05

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

Tidy up the select and order by location and we have new pick list

Page 145: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions145

Pick list

2012-12-05

LOC ITEM PICK_AT_LOC ORDNO QTY_FOR_ORD

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

1-A-02 B1 5 73 5

1-A-20 A1 18 51 18

1-B-11 B1 4 51 3

1-B-11 B1 4 73 1

1-B-15 B1 2 51 2

1-C-04 B1 12 51 12

1-C-05 A1 6 73 6

2-A-02 A1 24 51 6

2-A-02 A1 24 62 8

2-A-02 A1 24 73 10

2-D-23 B1 1 51 1

The operator now knows to pick 24 pieces of A1 at location 2-A-02 and distribute them with 6 for order 51, 8 for order 62 and 10 for order 73

Page 146: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions146

Pick list with route

2012-12-05

...(orderlines, orderbatch and fifo with clauses)...

), 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)...

Move the pick list into a fourth with clause

Add the ranking aisle_no calculation

Page 147: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions147

Pick list with route

2012-12-05

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

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

And so a big select of 4 with clauses and the final pick list of multiple orders by FIFO with efficient route

Page 148: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions148

Pick list with route

2012-12-05

LOC ITEM PICK_AT_LOC ORDNO QTY_FOR_ORD

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

1-A-02 B1 5 73 5

1-A-20 A1 18 51 18

1-B-15 B1 2 51 2

1-B-11 B1 4 51 3

1-B-11 B1 4 73 1

1-C-04 B1 12 51 12

1-C-05 A1 6 73 6

2-A-02 A1 24 51 6

2-A-02 A1 24 73 10

2-A-02 A1 24 62 8

2-D-23 B1 1 51 1

All done

What more can we wish for?

Page 149: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions149

Multi-order FIFO• Do FIFO on the sum of the orders• Calculate From/To qty intervals of picks• Calculate From/To qty intervals of orders• Join overlapping intervals

“Don’t you just love these kind of challenges?It’s why we do what we do!” – Monty Latiolais

2012-12-05

Page 150: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions150

Analytics forever…and ever and ever…

2012-12-05

Page 151: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions151

It never ends…

• We use analytic functions all the time

• We can’t imagine living without analytics

• WheelGuide®• Replenish shop stock• Call Center statistics• Spare parts guide• Customer count / work schedule / number

of orders• Booking calendar for mechanics• Shop space management• Discover idle hands• Detect seasonal variations for sales• Efficiency of Royal Danish Mail• …

2012-12-05

Page 152: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions152

Do It Yourself• Just start using analytics• The more you do the more often you find cases• When you start to think you need to process your data

procedurally – think again!• Use the power of SQL to let the database do the hard work

processing data• That’s what the database does best• And you’re paying for it so why not use it• If not – you are missing out on great functionality

2012-12-05

Page 153: Really using Oracle analytic SQL functions

#ukoug2012 Really Using Analytic Functions153

Any questions?• Download presentation

from UKOUG• Or you can get presentation

as well as the scripts at:

http://goo.gl/g46b4@kibeha [email protected]

http://dspsd.blogspot.com

2012-12-05

Page 154: Really using Oracle analytic SQL functions