The following is intended to outline our general product direction. It is intended for information purposes
only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code,
or functionality, and should not be relied upon in making purchasing decisions. The development,
release, timing, and pricing of any features or functionality described for Oracle’s products may change
and remains at the sole discretion of Oracle Corporation.
Statements in this presentation relating to Oracle’s future plans, expectations, beliefs, intentions and
prospects are “forward-looking statements” and are subject to material risks and uncertainties. A detailed
discussion of these factors and other risks that affect our business is contained in Oracle’s Securities and
Exchange Commission (SEC) filings, including our most recent reports on Form 10-K and Form 10-Q
under the heading “Risk Factors.” These filings are available on the SEC’s website or on Oracle’s website
at http://www.oracle.com/investor. All information in this presentation is current as of September 2019
and Oracle undertakes no duty to update any statement in light of new information or future events.
Safe Harbor
Copyright © 2019 Oracle and/or its affiliates.
SQL Windowing Functions
Dave Stokes
MySQL Community Manager
Oracle Corporation
Copyright © 2019 Oracle and/or its affiliates.
Window Functions
Window functions allow access to data in
the records right before and after the
current record.
A window function defines a frame or
window of rows with a given length around
the current row, and performs a calculation
across the set of data in the window.
Copyright © 2019 Oracle and/or its affiliates.
https://en.wikipedia.org/wiki/SQL_window_function
Windowing Functions
MySQL 8.0 added support for window
functions.
However we will start with what doing
calculations WITHOUT WFs looked like!
Copyright © 2019 Oracle and/or its affiliates.
Sample Data – w/o WF
Copyright © 2019 Oracle and/or its affiliates.
SQL > select a,b,c,d from w1;
+---+----+-----+------+
| a | b | c | d |
+---+----+-----+------+
| 1 | 10 | 100 | 1000 |
| 2 | 20 | 200 | 2000 |
| 3 | 30 | 300 | 3000 |
| 4 | 40 | 400 | 4000 |
+---+----+-----+------+
4 rows in set (0.0004 sec)
Try to add rows -> Opps!
Copyright © 2019 Oracle and/or its affiliates.
select a,b,c,d, sum(a+b) as 'a&b' from w1;
+---+----+-----+------+-----+
| a | b | c | d | a&b |
+---+----+-----+------+-----+
| 1 | 10 | 100 | 1000 | 110 |
+---+----+-----+------+-----+
1 row in set (0.0028 sec)
1+2+3+4+10+20+30+40 = 110
Tell server how to ‘group’ data -> Better!
Copyright © 2019 Oracle and/or its affiliates.
select a,b,c,d, sum(a+b) as 'a&b'
from w1
GROUP BY a;
+---+----+-----+------+-----+
| a | b | c | d | a&b |
+---+----+-----+------+-----+
| 1 | 10 | 100 | 1000 | 11 |
| 2 | 20 | 200 | 2000 | 22 |
| 3 | 30 | 300 | 3000 | 33 |
| 4 | 40 | 400 | 4000 | 44 |
+---+----+-----+------+-----+
4 rows in set (0.0019 sec)
We can sum columns but …
Copyright © 2019 Oracle and/or its affiliates.
select a, sum(a), b, sum(b) from w1;
+---+--------+----+--------+
| a | sum(a) | b | sum(b) |
+---+--------+----+--------+
| 1 | 10 | 10 | 100 |
+---+--------+----+--------+
We can sum the first value
from a row and the sum of
all ‘a’ values. But does that
first value really tell us
anything?
1+2+3+4 = 10
10+20+30+40 = 100
New Data
Copyright © 2019 Oracle and/or its affiliates.
SQL > select id, price, warehouse, vendor
from w2;
+----+-------+-----------+--------+
| id | price | warehouse | vendor |
+----+-------+-----------+--------+
| 1 | 1.99 | 1 | 1 |
| 2 | 10.50 | 1 | 2 |
| 3 | 0.99 | 2 | 2 |
| 4 | 1.10 | 1 | 2 |
+----+-------+-----------+--------+
Grouping and Rollup
Copyright © 2019 Oracle and/or its affiliates.
select warehouse,
sum(price)
from w2
group by warehouse WITH ROLLUP;
+-----------+------------+
| warehouse | sum(price) |
+-----------+------------+
| 1 | 13.59 |
| 2 | 0.99 |
| NULL | 14.58 |
+-----------+------------+
We can group like items
together and even ‘roll up’
values for totals.
The NULL under the
warehouse column is the
ROLLUP or total of the
sum(price) -- And not easily
understood
And we can use different columns
Copyright © 2019 Oracle and/or its affiliates.
SQL > select vendor,
sum(price)
from w2
group by vendor with rollup;
+--------+------------+
| vendor | sum(price) |
+--------+------------+
| 1 | 1.99 |
| 2 | 12.59 |
| NULL | 14.58 |
+--------+------------+
Non Windowing Aggregate Functions
• SUM
• COUNT
• MAX,MIN,
• STD, STDDEV,STDDEV_POP, STDDEV_SAMP
• VAR,VAR_POP, VARIANCE
• See https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_sum
Copyright © 2019 Oracle and/or its affiliates.
So Why Windowing Functions
A window function performs an aggregate-like operation
on a set of query rows. However, whereas an aggregate
operation groups query rows into a single result row, a
window function produces a result for each query row:
• The row for which function evaluation occurs is called
the current row.
• The query rows related to the current row over which
function evaluation occurs comprise the window for the
current row.
Copyright © 2019 Oracle and/or its affiliates.
Warning!
Windowing Functions are difficult – you need
to practice with them to build understanding
and competence.
Do not panic if you struggle at first – they are
a learned skilled.
Copyright © 2019 Oracle and/or its affiliates.
NULL
Null (or NULL) is a special marker used in Structured Query
Language to indicate that a data value does not exist in the
database. Introduced by the creator of the relational database model,
E. F. Codd, SQL Null serves to fulfil the requirement that all true
relational database management systems (RDBMS) support a
representation of "missing information and inapplicable information“
https://en.wikipedia.org/wiki/Null_(SQL)
Copyright © 2019 Oracle and/or its affiliates.
New Keyword - OVER
Copyright © 2019 Oracle and/or its affiliates.
SELECT year, country, product, profit,
SUM(profit) OVER() AS total_profit,
SUM(profit) OVER(PARTITION BY country) AS country_profit
FROM sales
ORDER BY country, year, product, profit;
+------+---------+------------+--------+--------------+----------------+
| year | country | product | profit | total_profit | country_profit |
+------+---------+------------+--------+--------------+----------------+
| 2000 | Finland | Computer | 1500 | 7535 | 1610 | 1610 = 1500+100+10 (Finland)
| 2000 | Finland | Phone | 100 | 7535 | 1610 |
| 2001 | Finland | Phone | 10 | 7535 | 1610 |
| 2000 | India | Calculator | 75 | 7535 | 1350 |
| 2000 | India | Calculator | 75 | 7535 | 1350 |
| 2000 | India | Computer | 1200 | 7535 | 1350 |
| 2000 | USA | Calculator | 75 | 7535 | 4575 |
| 2000 | USA | Computer | 1500 | 7535 | 4575 |
| 2001 | USA | Calculator | 50 | 7535 | 4575 |
| 2001 | USA | Computer | 1200 | 7535 | 4575 |
| 2001 | USA | Computer | 1500 | 7535 | 4575 |
| 2001 | USA | TV | 100 | 7535 | 4575 |
| 2001 | USA | TV | 150 | 7535 | 4575 |
+------+---------+------------+--------+--------------+----------------+
= 7535
New Keyword - OVER
Copyright © 2019 Oracle and/or its affiliates.
SELECT year, country, product, profit,
SUM(profit) OVER() AS total_profit,
SUM(profit) OVER(PARTITION BY country) AS country_profit
FROM sales
ORDER BY country, year, product, profit;
The first OVER() clause is empty which treats the entire set of rows as a
partition (global).
The second OVER() clause partitions rows by country, producing a sum per
partition (per country). The function produces this sum for each partition
row (country).
Supported Non Aggregate Functions
• CUME_DIST()
• DENSE_RANK()
• FIRST_VALUE()
• LAG(), LEAD()
• LAST_VALUE()
• NTH_VALUE()
• NTILE()
• PERCENT_RANK()
• RANK()
• ROW_NUMBER()
Copyright © 2019 Oracle and/or its affiliates.
ROW_NUMBER()
Copyright © 2019 Oracle and/or its affiliates.
SQL > select
ROW_NUMBER() over () as 'row nbr', b
FROM w1;
+---------+----+
| row nbr | b |
+---------+----+
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
+---------+----+
Rank and Dense Rank
Copyright © 2019 Oracle and/or its affiliates.
SELECT x, row_number() over (order by x) AS 'Row Nbr',
rank() over (order by x) AS 'Rank',
DENSE_RANK() over (order by x) as 'Dense Rank'
from w4;
+---+---------+------+------------+
| x | Row Nbr | Rank | Dense Rank |
+---+---------+------+------------+
| 0 | 1 | 1 | 1 |
| 0 | 2 | 1 | 1 |
| 2 | 3 | 3 | 2 |
| 3 | 4 | 4 | 3 |
| 3 | 5 | 4 | 3 |
| 4 | 6 | 6 | 4 |
+---+---------+------+------------+
+---+
| x |
+---+
| 0 |
| 0 |
| 2 |
| 3 |
| 3 |
| 4 |
+---+
Rank and Dense Rank – Messy repeat
Copyright © 2019 Oracle and/or its affiliates.
SELECT x, row_number() over (order by x) AS 'Row Nbr',
rank() over (order by x) AS 'Rank',
DENSE_RANK() over (order by x) as 'Dense Rank'
from w4;
+---+---------+------+------------+
| x | Row Nbr | Rank | Dense Rank |
+---+---------+------+------------+
| 0 | 1 | 1 | 1 |
| 0 | 2 | 1 | 1 |
| 2 | 3 | 3 | 2 |
| 3 | 4 | 4 | 3 |
| 3 | 5 | 4 | 3 |
| 4 | 6 | 6 | 4 |
+---+---------+------+------------+
+---+
| x |
+---+
| 0 |
| 0 |
| 2 |
| 3 |
| 3 |
| 4 |
+---+
Rank and Dense Rank – Named Window
Copyright © 2019 Oracle and/or its affiliates.
SELECT x, ROW_NUMBER() over w AS 'Row Nbr',
RANK() over w AS 'Rank',
DENSE_RANK() over w as 'Dense Rank'
from w4
WINDOW w as (order by x);
+---+---------+------+------------+
| x | Row Nbr | Rank | Dense Rank |
+---+---------+------+------------+
| 0 | 1 | 1 | 1 |
| 0 | 2 | 1 | 1 |
| 2 | 3 | 3 | 2 |
| 3 | 4 | 4 | 3 |
| 3 | 5 | 4 | 3 |
| 4 | 6 | 6 | 4 |
+---+---------+------+------------+
+---+
| x |
+---+
| 0 |
| 0 |
| 2 |
| 3 |
| 3 |
| 4 |
+---+
You Can Modify Window Clauses
Copyright © 2019 Oracle and/or its affiliates.
SELECT DISTINCT year,
country,
FIRST_VALUE(year) OVER (w ORDER BY year ASC) AS first,
FIRST_VALUE(year) OVER (w ORDER BY year DESC) AS last
FROM sales
WINDOW w AS (PARTITION BY country);
What if you double up?
select date,
name,
first_value(date) over (w order by name) as first
from sales
window w as (order by date);
ERROR: 3583: Window '<unnamed window>' cannot inherit 'w'
since both contain an ORDER BY clause.
Copyright © 2019 Oracle and/or its affiliates.
Another Data Set
Copyright © 2019 Oracle and/or its affiliates.
select * from sales;
+------+------------+-------+
| name | date | sales |
+------+------------+-------+
| Dave | 2019-01-01 | 125 |
| Jack | 2019-01-15 | 86 |
| Lucy | 2019-01-13 | 140 |
| Dave | 2019-03-03 | 100 |
| Lucy | 2019-04-01 | 200 |
| Dave | 2019-09-01 | 100 |
| Jack | 2019-09-15 | 95 |
| Lucy | 2019-09-13 | 140 |
| Jack | 2019-06-03 | 100 |
| Lucy | 2019-05-01 | 200 |
+------+------------+-------+
It is often common to want to know the
sales by name, calendar dates, ranges
of prices, and etcetera.
Another Data Set
Copyright © 2019 Oracle and/or its affiliates.
select * from sales;
+------+------------+-------+
| name | date | sales |
+------+------------+-------+
| Dave | 2019-01-01 | 125 |
| Jack | 2019-01-15 | 86 |
| Lucy | 2019-01-13 | 140 |
| Dave | 2019-03-03 | 100 |
| Lucy | 2019-04-01 | 200 |
| Dave | 2019-09-01 | 100 |
| Jack | 2019-09-15 | 95 |
| Lucy | 2019-09-13 | 140 |
| Jack | 2019-06-03 | 100 |
| Lucy | 2019-05-01 | 200 |
+------+------------+-------+
SELECT name,
sum(sales)
from sales
group by name;
+------+------------+
| name | sum(sales) |
+------+------------+
| Dave | 325 |
| Jack | 281 |
| Lucy | 680 |
+------+------------+
Another Data Set
Copyright © 2019 Oracle and/or its affiliates.
select * from sales;
+------+------------+-------+
| name | date | sales |
+------+------------+-------+
| Dave | 2019-01-01 | 125 |
| Jack | 2019-01-15 | 86 |
| Lucy | 2019-01-13 | 140 |
| Dave | 2019-03-03 | 100 |
| Lucy | 2019-04-01 | 200 |
| Dave | 2019-09-01 | 100 |
| Jack | 2019-09-15 | 95 |
| Lucy | 2019-09-13 | 140 |
| Jack | 2019-06-03 | 100 |
| Lucy | 2019-05-01 | 200 |
+------+------------+-------+
select name, date, sales,
SUM(sales) OVER (partition by name) as 'sum'
FROM sales;
+------+------------+-------+-----+
| name | date | sales | sum |
+------+------------+-------+-----+
| Dave | 2019-01-01 | 125 | 325 |
| Dave | 2019-03-03 | 100 | 325 |
| Dave | 2019-09-01 | 100 | 325 |
| Jack | 2019-01-15 | 86 | 281 |
| Jack | 2019-09-15 | 95 | 281 |
| Jack | 2019-06-03 | 100 | 281 |
| Lucy | 2019-01-13 | 140 | 680 |
| Lucy | 2019-04-01 | 200 | 680 |
| Lucy | 2019-09-13 | 140 | 680 |
| Lucy | 2019-05-01 | 200 | 680 |
+------+------------+-------+-----+
Monthly results
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, MONTHNAME(date), sales,
sum(sales) OVER (PARTITION BY MONTH(date)) as 'my sum'
FROM sales;
+------+-----------------+-------+--------+
| name | MONTHNAME(date) | sales | my sum |
+------+-----------------+-------+--------+
| Dave | January | 125 | 351 |
| Jack | January | 86 | 351 |
| Lucy | January | 140 | 351 |
| Dave | March | 100 | 100 |
| Lucy | April | 200 | 200 |
| Lucy | May | 200 | 200 |
| Jack | June | 100 | 100 |
| Dave | September | 100 | 335 |
| Jack | September | 95 | 335 |
| Lucy | September | 140 | 335 |
+------+-----------------+-------+--------+
Cumulative Sales by name
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, sales, date,
SUM(sales) OVER (PARTITION by name ORDER BY date) as cum_sales
FROM sales;
+------+-------+------------+-----------+
| name | sales | date | cum_sales |
+------+-------+------------+-----------+
| Dave | 125 | 2019-01-01 | 125 |
| Dave | 100 | 2019-03-03 | 225 |
| Dave | 100 | 2019-09-01 | 325 |
| Jack | 86 | 2019-01-15 | 86 |
| Jack | 100 | 2019-06-03 | 186 |
| Jack | 95 | 2019-09-15 | 281 |
| Lucy | 140 | 2019-01-13 | 140 |
| Lucy | 200 | 2019-04-01 | 340 |
| Lucy | 200 | 2019-05-01 | 540 |
| Lucy | 140 | 2019-09-13 | 680 |
+------+-------+------------+-----------+
Current, N, and Unbounded Rows
Copyright © 2019 Oracle and/or its affiliates.
Cumulative Sales by sale
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, sales, date,
sum(sales) over (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
as cum_sales
FROM sales;
+------+-------+------------+-----------+
| name | sales | date | cum_sales |
+------+-------+------------+-----------+
| Dave | 125 | 2019-01-01 | 125 | 125
| Lucy | 140 | 2019-01-13 | 265 | 125+140 = 265
| Jack | 86 | 2019-01-15 | 351 | 265 + 86 = 351
| Dave | 100 | 2019-03-03 | 451 |
| Lucy | 200 | 2019-04-01 | 651 |
| Lucy | 200 | 2019-05-01 | 851 |
| Jack | 100 | 2019-06-03 | 951 |
| Dave | 100 | 2019-09-01 | 1051 |
| Lucy | 140 | 2019-09-13 | 1191 |
| Jack | 95 | 2019-09-15 | 1286 |
+------+-------+------------+-----------+
Average Sales
Copyright © 2019 Oracle and/or its affiliates.
SELECT MONTH(date), SUM(sales),
AVG(SUM(sales)) OVER (ORDER BY MONTH(date) RANGE BETWEEN 1 PRECEDING and 1 FOLLOWING)
AS slidingAvg from sales group by MONTH(date);
+-------------+------------+------------+
| MONTH(date) | SUM(sales) | slidingAvg |
+-------------+------------+------------+
| 1 | 351 | 351.0000 | 351
| 3 | 100 | 150.0000 | 150 = (351+100) / 3
| 4 | 200 | 166.6667 | 166.6667 = (100+200+200) / 3
| 5 | 200 | 166.6667 |
| 6 | 100 | 150.0000 |
| 9 | 335 | 335.0000 |
+-------------+------------+------------+
Another Data Set
Copyright © 2019 Oracle and/or its affiliates.
select * from sales;
+------+------------+-------+
| name | date | sales |
+------+------------+-------+
| Dave | 2019-01-01 | 125 |
| Jack | 2019-01-15 | 86 |
| Lucy | 2019-01-13 | 140 |
| Dave | 2019-03-03 | 100 |
| Lucy | 2019-04-01 | 200 |
| Dave | 2019-09-01 | 100 |
| Jack | 2019-09-15 | 95 |
| Lucy | 2019-09-13 | 140 |
| Jack | 2019-06-03 | 100 |
| Lucy | 2019-05-01 | 200 |
+------+------------+-------+
select name, date, sales,
SUM(sales) OVER w as 'sum'
FROM sales
WINDOW w AS (partition by name order by date);
+------+------------+-------+-----+
| name | date | sales | sum |
+------+------------+-------+-----+
| Dave | 2019-01-01 | 125 | 125 |
| Dave | 2019-03-03 | 100 | 225 |
| Dave | 2019-09-01 | 100 | 325 |
| Jack | 2019-01-15 | 86 | 86 |
| Jack | 2019-06-03 | 100 | 186 |
| Jack | 2019-09-15 | 95 | 281 |
| Lucy | 2019-01-13 | 140 | 140 |
| Lucy | 2019-04-01 | 200 | 340 |
| Lucy | 2019-05-01 | 200 | 540 |
| Lucy | 2019-09-13 | 140 | 680 |
+------+------------+-------+-----+
Syntax
Copyright © 2019 Oracle and/or its affiliates.
over_clause:
{OVER (window_spec) | OVER window_name}
window_spec:
[window_name] [partition_clause] [order_clause]
[frame_clause]
partition_clause:
PARTITION BY expr [, expr] ...
order_clause:
ORDER BY expr [ASC|DESC] [, expr [ASC|DESC]] ...
Syntax
Copyright © 2019 Oracle and/or its affiliates.
frame_clause: frame_units frame_extent
frame_units: {ROWS | RANGE}
frame_extent:
{frame_start | frame_between}
frame_between:
BETWEEN frame_start AND frame_end frame_start, frame_end:
{ CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING |
expr PRECEDING | expr FOLLOWING }
Lets work a simple average w/ default
Copyright © 2019 Oracle and/or its affiliates.
select a, count(a) over w as 'count a',
b, avg(b) over w as 'avg b' from w1
window w as (order by a) ;
+---+---------+----+---------+
| a | count a | b | avg b |
+---+---------+----+---------+
| 1 | 1 | 10 | 10.0000 |
| 2 | 2 | 20 | 15.0000 | 15 = (10+20)/2
| 3 | 3 | 30 | 20.0000 | 30 = (10+20+30)/3
| 4 | 4 | 40 | 25.0000 | 25 = 100/4
+---+---------+----+---------+ Default is UNBOUNDED
PRECEDING to CURRENT
ROWS BETWEEN
Copyright © 2019 Oracle and/or its affiliates.
select a, count(a) over w as 'count a',
b, avg(b) over w as 'avg b' from w1
window w as
(ROWS BETWEEN 1 PRECEDING and CURRENT ROW) ;
+---+---------+----+---------+
| a | count a | b | avg b |
+---+---------+----+---------+
| 1 | 1 | 10 | 10.0000 |
| 2 | 2 | 20 | 15.0000 |
| 3 | 2 | 30 | 25.0000 | 25 = (20+30) / 2
| 4 | 2 | 40 | 35.0000 | 35 = (30+40) / 2
ROWS BETWEEN
Copyright © 2019 Oracle and/or its affiliates.
select a, count(a) over w as 'count a',
b, avg(b) over w as 'avg b' from w1
window w as
(ROWS BETWEEN UNBOUNDED PRECEDING and CURRENT ROW) ;
+---+---------+----+---------+
| a | count a | b | avg b |
+---+---------+----+---------+
| 1 | 1 | 10 | 10.0000 |
| 2 | 2 | 20 | 15.0000 |
| 3 | 3 | 30 | 20.0000 |
| 4 | 4 | 40 | 25.0000 |
ROWS BETWEEN
Copyright © 2019 Oracle and/or its affiliates.
select a, count(a) over w as 'count a',
b, avg(b) over w as 'avg b' from w1
window w as (PARTITION BY a >= 3) ;
+---+---------+----+---------+
| a | count a | b | avg b |
+---+---------+----+---------+
| 1 | 2 | 10 | 15.0000 | 15 = (10+20)/2
| 2 | 2 | 20 | 15.0000 |
| 3 | 2 | 30 | 35.0000 | Start of PARTITION
| 4 | 2 | 40 | 35.0000 | 35 = (30+40)/2
LAG – values from preceding (x) rows
Copyright © 2019 Oracle and/or its affiliates.
select a, lag(a,1) over w as 'lag(1)',
lag(a,2) over w as 'lag(2)' from w1
window w as (order by a);
+---+--------+--------+
| a | lag(1) | lag(2) |
+---+--------+--------+
| 1 | NULL | NULL |
| 2 | 1 | NULL |
| 3 | 2 | 1 |
| 4 | 3 | 2 |
+---+--------+--------+
LEAD – values from preceding (x) rows
Copyright © 2019 Oracle and/or its affiliates.
select a, lead(a,1) over w as 'lead(1)',
lead(a,2) over w as 'lead(2)' from w1
window w as (order by a);
+---+---------+---------+
| a | lead(1) | lead(2) |
+---+---------+---------+
| 1 | 2 | 3 |
| 2 | 3 | 4 |
| 3 | 4 | NULL |
| 4 | NULL | NULL |
First and Last Values
Copyright © 2019 Oracle and/or its affiliates.
select a, FIRST_VALUE(a) over w as 'first value',
LAST_VALUE(a) over w as 'last value'
from w1 window w as (order by a);
+---+-------------+------------+
| a | first value | last value |
+---+-------------+------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
N Values
Copyright © 2019 Oracle and/or its affiliates.
select a,
NTH_VALUE(a,2) over w as 'n2',
NTH_VALUE(a,3) over w as 'n3' from w1 window w as
(order by a);
+---+------+------+
| a | n2 | n3 |
+---+------+------+
| 1 | NULL | NULL | No 2nd or 3rd row read so no values
| 2 | 2 | NULL | No 3rd row read so no values
| 3 | 2 | 3 |
| 4 | 2 | 3 |
Another Example
Copyright © 2019 Oracle and/or its affiliates.
SELECT * FROM staff ORDER by id;
+----+-------+-----------+---------+
| id | name | dept | salary |
+----+-------+-----------+---------+
| 1 | Andy | Shipping | 5400.00 |
| 2 | Betty | Marketing | 6300.00 |
| 3 | Tracy | Shipping | 4800.00 |
| 4 | Mike | Marketing | 7100.00 |
| 5 | Sandy | Sales | 5400.00 |
| 6 | James | Shipping | 6000.00 |
| 7 | Carol | Sales | 4600.00 |
Another Example
Copyright © 2019 Oracle and/or its affiliates.
SELECT COUNT(*), SUM(salary) FROM staff;
+----------+-------------+
| COUNT(*) | SUM(salary) |
+----------+-------------+
| 7 | 39600.00 |
+----------+-------------+
Another Example
Copyright © 2019 Oracle and/or its affiliates.
SELECT COUNT(*) AS 'nbr' ,
SUM(salary) as 'tot salary',
ROUND(avg(salary),0) AS avg
FROM staff
GROUP BY dept ORDER BY dept;
+-----+------------+------+
| nbr | tot salary | avg |
+-----+------------+------+
| 2 | 13400.00 | 6700 |
| 2 | 10000.00 | 5000 |
| 3 | 16200.00 | 5400 |
Another Example
Copyright © 2019 Oracle and/or its affiliates.
SELECT dept, COUNT(*) AS 'nbr' ,
SUM(salary) as 'tot salary',
ROUND(avg(salary),0) AS avg
FROM staff
GROUP BY dept WITH ROLLUP order by dept;
+-----------+-----+------------+------+
| dept | nbr | tot salary | avg |
+-----------+-----+------------+------+
| NULL | 7 | 39600.00 | 5657 |
| Marketing | 2 | 13400.00 | 6700 |
| Sales | 2 | 10000.00 | 5000 |
| Shipping | 3 | 16200.00 | 5400 |
Another Example
Copyright © 2019 Oracle and/or its affiliates.
SELECT dept, COUNT(*) AS 'nbr' ,
SUM(salary) as 'tot salary',
ROUND(avg(salary),0) AS avg
FROM staff
GROUP BY dept WITH ROLLUP order by dept;
+-----------+-----+------------+------+
| dept | nbr | tot salary | avg |
+-----------+-----+------------+------+
| NULL | 7 | 39600.00 | 5657 |
| Marketing | 2 | 13400.00 | 6700 |
| Sales | 2 | 10000.00 | 5000 |
| Shipping | 3 | 16200.00 | 5400 |
Calculate Rank
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, salary,
RANK() OVER s AS 'Rank' ,
DENSE_RANK() OVER s AS ‘Dense Rank'
FROM staff
WINDOW s as (ORDER BY salary DESC) ;
+-------+---------+------+-------------+
| name | salary | Rank | Dense Rank |
+-------+---------+------+-------------+
| Mike | 7100.00 | 1 | 1 |
| Betty | 6300.00 | 2 | 2 |
| James | 6000.00 | 3 | 3 |
| Sandy | 5400.00 | 4 | 4 |
| Andy | 5400.00 | 4 | 4 |
| Tracy | 4800.00 | 6 | 5 |
| Carol | 4600.00 | 7 | 6 |
Percentage of Total Salary
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, salary,
ROUND(salary / SUM(salary) OVER() * 100,2) AS '%'
FROM staff ORDER BY salary DESC;
+-------+---------+-------+
| name | salary | % |
+-------+---------+-------+
| Mike | 7100.00 | 17.93 |
| Betty | 6300.00 | 15.91 |
| James | 6000.00 | 15.15 |
| Andy | 5400.00 | 13.64 |
| Sandy | 5400.00 | 13.64 |
| Tracy | 4800.00 | 12.12 |
| Carol | 4600.00 | 11.62 |
Difference to AVG Salary
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, salary,
ROUND(AVG(salary) OVER (),2) AS 'avg',
ROUND(salary - AVG(salary) OVER (),2) AS 'Diff to AVG'
FROM staff ORDER BY salary DESC;
+-------+---------+---------+-------------+
| name | salary | avg | Diff to AVG |
+-------+---------+---------+-------------+
| Mike | 7100.00 | 5657.14 | 1442.86 |
| Betty | 6300.00 | 5657.14 | 642.86 |
| James | 6000.00 | 5657.14 | 342.86 |
| Andy | 5400.00 | 5657.14 | -257.14 |
| Sandy | 5400.00 | 5657.14 | -257.14 |
| Tracy | 4800.00 | 5657.14 | -857.14 |
| Carol | 4600.00 | 5657.14 | -1057.14 |
Calculate differences in salary
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, salary,
salary - LEAD(salary,1) OVER (ORDER BY salary DESC) as 'diff next'
FROM staff ORDER BY salary DESC;
+-------+---------+-----------+
| name | salary | diff next |
+-------+---------+-----------+
| Mike | 7100.00 | 800.00 |
| Betty | 6300.00 | 300.00 |
| James | 6000.00 | 600.00 |
| Andy | 5400.00 | 0.00 |
| Sandy | 5400.00 | 600.00 |
| Tracy | 4800.00 | 200.00 |
| Carol | 4600.00 | NULL |
Calculate differences in salary
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, salary,
salary - LAST_VALUE(salary) OVER w AS 'above',
ROUND((salary - LAST_VALUE(salary) OVER w) / LAST_VALUE(salary)
OVER w * 100) AS 'pct above'
FROM staff
WINDOW w as (ORDER by salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING) ORDER BY salary DESC;
+-------+---------+---------+-----------+
| name | salary | above | pct above |
+-------+---------+---------+-----------+
| Mike | 7100.00 | 2500.00 | 54 |
| Betty | 6300.00 | 1700.00 | 37 |
| James | 6000.00 | 1400.00 | 30 |
| Sandy | 5400.00 | 800.00 | 17 |
| Andy | 5400.00 | 800.00 | 17 |
| Tracy | 4800.00 | 200.00 | 4 |
| Carol | 4600.00 | 0.00 | 0 |
Calculate differences in salary
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, dept, salary,
round(AVG(salary) OVER z,2) AS 'Avg',
round(salary - AVG(salary) OVER z,2) as 'diff avg'
FROM staff WINDOW z AS (PARTITION BY dept)
ORDER BY dept, salary DESC;
+-------+-----------+---------+---------+----------+
| name | dept | salary | Avg | diff avg |
+-------+-----------+---------+---------+----------+
| Mike | Marketing | 7100.00 | 6700.00 | 400.00 |
| Betty | Marketing | 6300.00 | 6700.00 | -400.00 |
| Sandy | Sales | 5400.00 | 5000.00 | 400.00 |
| Carol | Sales | 4600.00 | 5000.00 | -400.00 |
| James | Shipping | 6000.00 | 5400.00 | 600.00 |
| Andy | Shipping | 5400.00 | 5400.00 | 0.00 |
| Tracy | Shipping | 4800.00 | 5400.00 | -600.00 |
Company and Departmental Ranks
Copyright © 2019 Oracle and/or its affiliates.
SELECT name, dept, salary,
RANK() OVER s AS dept_rank,
RANK() OVER (ORDER BY salary DESC) AS Company_rank
FROM staff
WINDOW s as (PARTITION BY dept ORDER BY salary DESC)
ORDER BY dept, salary DESC;
+-------+-----------+---------+-----------+--------------+
| name | dept | salary | dept_rank | Company_rank |
+-------+-----------+---------+-----------+--------------+
| Mike | Marketing | 7100.00 | 1 | 1 |
| Betty | Marketing | 6300.00 | 2 | 2 |
| Sandy | Sales | 5400.00 | 1 | 4 |
| Carol | Sales | 4600.00 | 2 | 7 |
| James | Shipping | 6000.00 | 1 | 3 |
| Andy | Shipping | 5400.00 | 2 | 4 |
| Tracy | Shipping | 4800.00 | 3 | 6 |
Good Place to Stop .. for now.
Copyright © 2019 Oracle and/or its affiliates.
Excellent Tutorial on Windowing Functions
https://momjian.us/main/writings/pgsql/window.pdf
Copyright © 2019 Oracle and/or its affiliates.
MySQL Windowing Functions
https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
Copyright © 2019 Oracle and/or its affiliates.
Please buy my Book!
Copyright © 2019 Oracle and/or its affiliates.
Q/A
Copyright © 2019 Oracle and/or its affiliates.
Slides at Slideshare.net/davidmstokes
Email at David.Stokes @ Oracle.com
Blog at https://elephantdolphin.blogspot.com/

Windowing Functions - Little Rock Tech fest 2019

  • 1.
    The following isintended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, timing, and pricing of any features or functionality described for Oracle’s products may change and remains at the sole discretion of Oracle Corporation. Statements in this presentation relating to Oracle’s future plans, expectations, beliefs, intentions and prospects are “forward-looking statements” and are subject to material risks and uncertainties. A detailed discussion of these factors and other risks that affect our business is contained in Oracle’s Securities and Exchange Commission (SEC) filings, including our most recent reports on Form 10-K and Form 10-Q under the heading “Risk Factors.” These filings are available on the SEC’s website or on Oracle’s website at http://www.oracle.com/investor. All information in this presentation is current as of September 2019 and Oracle undertakes no duty to update any statement in light of new information or future events. Safe Harbor Copyright © 2019 Oracle and/or its affiliates.
  • 2.
    SQL Windowing Functions DaveStokes MySQL Community Manager Oracle Corporation Copyright © 2019 Oracle and/or its affiliates.
  • 3.
    Window Functions Window functionsallow access to data in the records right before and after the current record. A window function defines a frame or window of rows with a given length around the current row, and performs a calculation across the set of data in the window. Copyright © 2019 Oracle and/or its affiliates. https://en.wikipedia.org/wiki/SQL_window_function
  • 4.
    Windowing Functions MySQL 8.0added support for window functions. However we will start with what doing calculations WITHOUT WFs looked like! Copyright © 2019 Oracle and/or its affiliates.
  • 5.
    Sample Data –w/o WF Copyright © 2019 Oracle and/or its affiliates. SQL > select a,b,c,d from w1; +---+----+-----+------+ | a | b | c | d | +---+----+-----+------+ | 1 | 10 | 100 | 1000 | | 2 | 20 | 200 | 2000 | | 3 | 30 | 300 | 3000 | | 4 | 40 | 400 | 4000 | +---+----+-----+------+ 4 rows in set (0.0004 sec)
  • 6.
    Try to addrows -> Opps! Copyright © 2019 Oracle and/or its affiliates. select a,b,c,d, sum(a+b) as 'a&b' from w1; +---+----+-----+------+-----+ | a | b | c | d | a&b | +---+----+-----+------+-----+ | 1 | 10 | 100 | 1000 | 110 | +---+----+-----+------+-----+ 1 row in set (0.0028 sec) 1+2+3+4+10+20+30+40 = 110
  • 7.
    Tell server howto ‘group’ data -> Better! Copyright © 2019 Oracle and/or its affiliates. select a,b,c,d, sum(a+b) as 'a&b' from w1 GROUP BY a; +---+----+-----+------+-----+ | a | b | c | d | a&b | +---+----+-----+------+-----+ | 1 | 10 | 100 | 1000 | 11 | | 2 | 20 | 200 | 2000 | 22 | | 3 | 30 | 300 | 3000 | 33 | | 4 | 40 | 400 | 4000 | 44 | +---+----+-----+------+-----+ 4 rows in set (0.0019 sec)
  • 8.
    We can sumcolumns but … Copyright © 2019 Oracle and/or its affiliates. select a, sum(a), b, sum(b) from w1; +---+--------+----+--------+ | a | sum(a) | b | sum(b) | +---+--------+----+--------+ | 1 | 10 | 10 | 100 | +---+--------+----+--------+ We can sum the first value from a row and the sum of all ‘a’ values. But does that first value really tell us anything? 1+2+3+4 = 10 10+20+30+40 = 100
  • 9.
    New Data Copyright ©2019 Oracle and/or its affiliates. SQL > select id, price, warehouse, vendor from w2; +----+-------+-----------+--------+ | id | price | warehouse | vendor | +----+-------+-----------+--------+ | 1 | 1.99 | 1 | 1 | | 2 | 10.50 | 1 | 2 | | 3 | 0.99 | 2 | 2 | | 4 | 1.10 | 1 | 2 | +----+-------+-----------+--------+
  • 10.
    Grouping and Rollup Copyright© 2019 Oracle and/or its affiliates. select warehouse, sum(price) from w2 group by warehouse WITH ROLLUP; +-----------+------------+ | warehouse | sum(price) | +-----------+------------+ | 1 | 13.59 | | 2 | 0.99 | | NULL | 14.58 | +-----------+------------+ We can group like items together and even ‘roll up’ values for totals. The NULL under the warehouse column is the ROLLUP or total of the sum(price) -- And not easily understood
  • 11.
    And we canuse different columns Copyright © 2019 Oracle and/or its affiliates. SQL > select vendor, sum(price) from w2 group by vendor with rollup; +--------+------------+ | vendor | sum(price) | +--------+------------+ | 1 | 1.99 | | 2 | 12.59 | | NULL | 14.58 | +--------+------------+
  • 12.
    Non Windowing AggregateFunctions • SUM • COUNT • MAX,MIN, • STD, STDDEV,STDDEV_POP, STDDEV_SAMP • VAR,VAR_POP, VARIANCE • See https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_sum Copyright © 2019 Oracle and/or its affiliates.
  • 13.
    So Why WindowingFunctions A window function performs an aggregate-like operation on a set of query rows. However, whereas an aggregate operation groups query rows into a single result row, a window function produces a result for each query row: • The row for which function evaluation occurs is called the current row. • The query rows related to the current row over which function evaluation occurs comprise the window for the current row. Copyright © 2019 Oracle and/or its affiliates.
  • 14.
    Warning! Windowing Functions aredifficult – you need to practice with them to build understanding and competence. Do not panic if you struggle at first – they are a learned skilled. Copyright © 2019 Oracle and/or its affiliates.
  • 15.
    NULL Null (or NULL)is a special marker used in Structured Query Language to indicate that a data value does not exist in the database. Introduced by the creator of the relational database model, E. F. Codd, SQL Null serves to fulfil the requirement that all true relational database management systems (RDBMS) support a representation of "missing information and inapplicable information“ https://en.wikipedia.org/wiki/Null_(SQL) Copyright © 2019 Oracle and/or its affiliates.
  • 16.
    New Keyword -OVER Copyright © 2019 Oracle and/or its affiliates. SELECT year, country, product, profit, SUM(profit) OVER() AS total_profit, SUM(profit) OVER(PARTITION BY country) AS country_profit FROM sales ORDER BY country, year, product, profit; +------+---------+------------+--------+--------------+----------------+ | year | country | product | profit | total_profit | country_profit | +------+---------+------------+--------+--------------+----------------+ | 2000 | Finland | Computer | 1500 | 7535 | 1610 | 1610 = 1500+100+10 (Finland) | 2000 | Finland | Phone | 100 | 7535 | 1610 | | 2001 | Finland | Phone | 10 | 7535 | 1610 | | 2000 | India | Calculator | 75 | 7535 | 1350 | | 2000 | India | Calculator | 75 | 7535 | 1350 | | 2000 | India | Computer | 1200 | 7535 | 1350 | | 2000 | USA | Calculator | 75 | 7535 | 4575 | | 2000 | USA | Computer | 1500 | 7535 | 4575 | | 2001 | USA | Calculator | 50 | 7535 | 4575 | | 2001 | USA | Computer | 1200 | 7535 | 4575 | | 2001 | USA | Computer | 1500 | 7535 | 4575 | | 2001 | USA | TV | 100 | 7535 | 4575 | | 2001 | USA | TV | 150 | 7535 | 4575 | +------+---------+------------+--------+--------------+----------------+ = 7535
  • 17.
    New Keyword -OVER Copyright © 2019 Oracle and/or its affiliates. SELECT year, country, product, profit, SUM(profit) OVER() AS total_profit, SUM(profit) OVER(PARTITION BY country) AS country_profit FROM sales ORDER BY country, year, product, profit; The first OVER() clause is empty which treats the entire set of rows as a partition (global). The second OVER() clause partitions rows by country, producing a sum per partition (per country). The function produces this sum for each partition row (country).
  • 18.
    Supported Non AggregateFunctions • CUME_DIST() • DENSE_RANK() • FIRST_VALUE() • LAG(), LEAD() • LAST_VALUE() • NTH_VALUE() • NTILE() • PERCENT_RANK() • RANK() • ROW_NUMBER() Copyright © 2019 Oracle and/or its affiliates.
  • 19.
    ROW_NUMBER() Copyright © 2019Oracle and/or its affiliates. SQL > select ROW_NUMBER() over () as 'row nbr', b FROM w1; +---------+----+ | row nbr | b | +---------+----+ | 1 | 10 | | 2 | 20 | | 3 | 30 | | 4 | 40 | +---------+----+
  • 20.
    Rank and DenseRank Copyright © 2019 Oracle and/or its affiliates. SELECT x, row_number() over (order by x) AS 'Row Nbr', rank() over (order by x) AS 'Rank', DENSE_RANK() over (order by x) as 'Dense Rank' from w4; +---+---------+------+------------+ | x | Row Nbr | Rank | Dense Rank | +---+---------+------+------------+ | 0 | 1 | 1 | 1 | | 0 | 2 | 1 | 1 | | 2 | 3 | 3 | 2 | | 3 | 4 | 4 | 3 | | 3 | 5 | 4 | 3 | | 4 | 6 | 6 | 4 | +---+---------+------+------------+ +---+ | x | +---+ | 0 | | 0 | | 2 | | 3 | | 3 | | 4 | +---+
  • 21.
    Rank and DenseRank – Messy repeat Copyright © 2019 Oracle and/or its affiliates. SELECT x, row_number() over (order by x) AS 'Row Nbr', rank() over (order by x) AS 'Rank', DENSE_RANK() over (order by x) as 'Dense Rank' from w4; +---+---------+------+------------+ | x | Row Nbr | Rank | Dense Rank | +---+---------+------+------------+ | 0 | 1 | 1 | 1 | | 0 | 2 | 1 | 1 | | 2 | 3 | 3 | 2 | | 3 | 4 | 4 | 3 | | 3 | 5 | 4 | 3 | | 4 | 6 | 6 | 4 | +---+---------+------+------------+ +---+ | x | +---+ | 0 | | 0 | | 2 | | 3 | | 3 | | 4 | +---+
  • 22.
    Rank and DenseRank – Named Window Copyright © 2019 Oracle and/or its affiliates. SELECT x, ROW_NUMBER() over w AS 'Row Nbr', RANK() over w AS 'Rank', DENSE_RANK() over w as 'Dense Rank' from w4 WINDOW w as (order by x); +---+---------+------+------------+ | x | Row Nbr | Rank | Dense Rank | +---+---------+------+------------+ | 0 | 1 | 1 | 1 | | 0 | 2 | 1 | 1 | | 2 | 3 | 3 | 2 | | 3 | 4 | 4 | 3 | | 3 | 5 | 4 | 3 | | 4 | 6 | 6 | 4 | +---+---------+------+------------+ +---+ | x | +---+ | 0 | | 0 | | 2 | | 3 | | 3 | | 4 | +---+
  • 23.
    You Can ModifyWindow Clauses Copyright © 2019 Oracle and/or its affiliates. SELECT DISTINCT year, country, FIRST_VALUE(year) OVER (w ORDER BY year ASC) AS first, FIRST_VALUE(year) OVER (w ORDER BY year DESC) AS last FROM sales WINDOW w AS (PARTITION BY country);
  • 24.
    What if youdouble up? select date, name, first_value(date) over (w order by name) as first from sales window w as (order by date); ERROR: 3583: Window '<unnamed window>' cannot inherit 'w' since both contain an ORDER BY clause. Copyright © 2019 Oracle and/or its affiliates.
  • 25.
    Another Data Set Copyright© 2019 Oracle and/or its affiliates. select * from sales; +------+------------+-------+ | name | date | sales | +------+------------+-------+ | Dave | 2019-01-01 | 125 | | Jack | 2019-01-15 | 86 | | Lucy | 2019-01-13 | 140 | | Dave | 2019-03-03 | 100 | | Lucy | 2019-04-01 | 200 | | Dave | 2019-09-01 | 100 | | Jack | 2019-09-15 | 95 | | Lucy | 2019-09-13 | 140 | | Jack | 2019-06-03 | 100 | | Lucy | 2019-05-01 | 200 | +------+------------+-------+ It is often common to want to know the sales by name, calendar dates, ranges of prices, and etcetera.
  • 26.
    Another Data Set Copyright© 2019 Oracle and/or its affiliates. select * from sales; +------+------------+-------+ | name | date | sales | +------+------------+-------+ | Dave | 2019-01-01 | 125 | | Jack | 2019-01-15 | 86 | | Lucy | 2019-01-13 | 140 | | Dave | 2019-03-03 | 100 | | Lucy | 2019-04-01 | 200 | | Dave | 2019-09-01 | 100 | | Jack | 2019-09-15 | 95 | | Lucy | 2019-09-13 | 140 | | Jack | 2019-06-03 | 100 | | Lucy | 2019-05-01 | 200 | +------+------------+-------+ SELECT name, sum(sales) from sales group by name; +------+------------+ | name | sum(sales) | +------+------------+ | Dave | 325 | | Jack | 281 | | Lucy | 680 | +------+------------+
  • 27.
    Another Data Set Copyright© 2019 Oracle and/or its affiliates. select * from sales; +------+------------+-------+ | name | date | sales | +------+------------+-------+ | Dave | 2019-01-01 | 125 | | Jack | 2019-01-15 | 86 | | Lucy | 2019-01-13 | 140 | | Dave | 2019-03-03 | 100 | | Lucy | 2019-04-01 | 200 | | Dave | 2019-09-01 | 100 | | Jack | 2019-09-15 | 95 | | Lucy | 2019-09-13 | 140 | | Jack | 2019-06-03 | 100 | | Lucy | 2019-05-01 | 200 | +------+------------+-------+ select name, date, sales, SUM(sales) OVER (partition by name) as 'sum' FROM sales; +------+------------+-------+-----+ | name | date | sales | sum | +------+------------+-------+-----+ | Dave | 2019-01-01 | 125 | 325 | | Dave | 2019-03-03 | 100 | 325 | | Dave | 2019-09-01 | 100 | 325 | | Jack | 2019-01-15 | 86 | 281 | | Jack | 2019-09-15 | 95 | 281 | | Jack | 2019-06-03 | 100 | 281 | | Lucy | 2019-01-13 | 140 | 680 | | Lucy | 2019-04-01 | 200 | 680 | | Lucy | 2019-09-13 | 140 | 680 | | Lucy | 2019-05-01 | 200 | 680 | +------+------------+-------+-----+
  • 28.
    Monthly results Copyright ©2019 Oracle and/or its affiliates. SELECT name, MONTHNAME(date), sales, sum(sales) OVER (PARTITION BY MONTH(date)) as 'my sum' FROM sales; +------+-----------------+-------+--------+ | name | MONTHNAME(date) | sales | my sum | +------+-----------------+-------+--------+ | Dave | January | 125 | 351 | | Jack | January | 86 | 351 | | Lucy | January | 140 | 351 | | Dave | March | 100 | 100 | | Lucy | April | 200 | 200 | | Lucy | May | 200 | 200 | | Jack | June | 100 | 100 | | Dave | September | 100 | 335 | | Jack | September | 95 | 335 | | Lucy | September | 140 | 335 | +------+-----------------+-------+--------+
  • 29.
    Cumulative Sales byname Copyright © 2019 Oracle and/or its affiliates. SELECT name, sales, date, SUM(sales) OVER (PARTITION by name ORDER BY date) as cum_sales FROM sales; +------+-------+------------+-----------+ | name | sales | date | cum_sales | +------+-------+------------+-----------+ | Dave | 125 | 2019-01-01 | 125 | | Dave | 100 | 2019-03-03 | 225 | | Dave | 100 | 2019-09-01 | 325 | | Jack | 86 | 2019-01-15 | 86 | | Jack | 100 | 2019-06-03 | 186 | | Jack | 95 | 2019-09-15 | 281 | | Lucy | 140 | 2019-01-13 | 140 | | Lucy | 200 | 2019-04-01 | 340 | | Lucy | 200 | 2019-05-01 | 540 | | Lucy | 140 | 2019-09-13 | 680 | +------+-------+------------+-----------+
  • 30.
    Current, N, andUnbounded Rows Copyright © 2019 Oracle and/or its affiliates.
  • 31.
    Cumulative Sales bysale Copyright © 2019 Oracle and/or its affiliates. SELECT name, sales, date, sum(sales) over (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as cum_sales FROM sales; +------+-------+------------+-----------+ | name | sales | date | cum_sales | +------+-------+------------+-----------+ | Dave | 125 | 2019-01-01 | 125 | 125 | Lucy | 140 | 2019-01-13 | 265 | 125+140 = 265 | Jack | 86 | 2019-01-15 | 351 | 265 + 86 = 351 | Dave | 100 | 2019-03-03 | 451 | | Lucy | 200 | 2019-04-01 | 651 | | Lucy | 200 | 2019-05-01 | 851 | | Jack | 100 | 2019-06-03 | 951 | | Dave | 100 | 2019-09-01 | 1051 | | Lucy | 140 | 2019-09-13 | 1191 | | Jack | 95 | 2019-09-15 | 1286 | +------+-------+------------+-----------+
  • 32.
    Average Sales Copyright ©2019 Oracle and/or its affiliates. SELECT MONTH(date), SUM(sales), AVG(SUM(sales)) OVER (ORDER BY MONTH(date) RANGE BETWEEN 1 PRECEDING and 1 FOLLOWING) AS slidingAvg from sales group by MONTH(date); +-------------+------------+------------+ | MONTH(date) | SUM(sales) | slidingAvg | +-------------+------------+------------+ | 1 | 351 | 351.0000 | 351 | 3 | 100 | 150.0000 | 150 = (351+100) / 3 | 4 | 200 | 166.6667 | 166.6667 = (100+200+200) / 3 | 5 | 200 | 166.6667 | | 6 | 100 | 150.0000 | | 9 | 335 | 335.0000 | +-------------+------------+------------+
  • 33.
    Another Data Set Copyright© 2019 Oracle and/or its affiliates. select * from sales; +------+------------+-------+ | name | date | sales | +------+------------+-------+ | Dave | 2019-01-01 | 125 | | Jack | 2019-01-15 | 86 | | Lucy | 2019-01-13 | 140 | | Dave | 2019-03-03 | 100 | | Lucy | 2019-04-01 | 200 | | Dave | 2019-09-01 | 100 | | Jack | 2019-09-15 | 95 | | Lucy | 2019-09-13 | 140 | | Jack | 2019-06-03 | 100 | | Lucy | 2019-05-01 | 200 | +------+------------+-------+ select name, date, sales, SUM(sales) OVER w as 'sum' FROM sales WINDOW w AS (partition by name order by date); +------+------------+-------+-----+ | name | date | sales | sum | +------+------------+-------+-----+ | Dave | 2019-01-01 | 125 | 125 | | Dave | 2019-03-03 | 100 | 225 | | Dave | 2019-09-01 | 100 | 325 | | Jack | 2019-01-15 | 86 | 86 | | Jack | 2019-06-03 | 100 | 186 | | Jack | 2019-09-15 | 95 | 281 | | Lucy | 2019-01-13 | 140 | 140 | | Lucy | 2019-04-01 | 200 | 340 | | Lucy | 2019-05-01 | 200 | 540 | | Lucy | 2019-09-13 | 140 | 680 | +------+------------+-------+-----+
  • 34.
    Syntax Copyright © 2019Oracle and/or its affiliates. over_clause: {OVER (window_spec) | OVER window_name} window_spec: [window_name] [partition_clause] [order_clause] [frame_clause] partition_clause: PARTITION BY expr [, expr] ... order_clause: ORDER BY expr [ASC|DESC] [, expr [ASC|DESC]] ...
  • 35.
    Syntax Copyright © 2019Oracle and/or its affiliates. frame_clause: frame_units frame_extent frame_units: {ROWS | RANGE} frame_extent: {frame_start | frame_between} frame_between: BETWEEN frame_start AND frame_end frame_start, frame_end: { CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING | expr PRECEDING | expr FOLLOWING }
  • 36.
    Lets work asimple average w/ default Copyright © 2019 Oracle and/or its affiliates. select a, count(a) over w as 'count a', b, avg(b) over w as 'avg b' from w1 window w as (order by a) ; +---+---------+----+---------+ | a | count a | b | avg b | +---+---------+----+---------+ | 1 | 1 | 10 | 10.0000 | | 2 | 2 | 20 | 15.0000 | 15 = (10+20)/2 | 3 | 3 | 30 | 20.0000 | 30 = (10+20+30)/3 | 4 | 4 | 40 | 25.0000 | 25 = 100/4 +---+---------+----+---------+ Default is UNBOUNDED PRECEDING to CURRENT
  • 37.
    ROWS BETWEEN Copyright ©2019 Oracle and/or its affiliates. select a, count(a) over w as 'count a', b, avg(b) over w as 'avg b' from w1 window w as (ROWS BETWEEN 1 PRECEDING and CURRENT ROW) ; +---+---------+----+---------+ | a | count a | b | avg b | +---+---------+----+---------+ | 1 | 1 | 10 | 10.0000 | | 2 | 2 | 20 | 15.0000 | | 3 | 2 | 30 | 25.0000 | 25 = (20+30) / 2 | 4 | 2 | 40 | 35.0000 | 35 = (30+40) / 2
  • 38.
    ROWS BETWEEN Copyright ©2019 Oracle and/or its affiliates. select a, count(a) over w as 'count a', b, avg(b) over w as 'avg b' from w1 window w as (ROWS BETWEEN UNBOUNDED PRECEDING and CURRENT ROW) ; +---+---------+----+---------+ | a | count a | b | avg b | +---+---------+----+---------+ | 1 | 1 | 10 | 10.0000 | | 2 | 2 | 20 | 15.0000 | | 3 | 3 | 30 | 20.0000 | | 4 | 4 | 40 | 25.0000 |
  • 39.
    ROWS BETWEEN Copyright ©2019 Oracle and/or its affiliates. select a, count(a) over w as 'count a', b, avg(b) over w as 'avg b' from w1 window w as (PARTITION BY a >= 3) ; +---+---------+----+---------+ | a | count a | b | avg b | +---+---------+----+---------+ | 1 | 2 | 10 | 15.0000 | 15 = (10+20)/2 | 2 | 2 | 20 | 15.0000 | | 3 | 2 | 30 | 35.0000 | Start of PARTITION | 4 | 2 | 40 | 35.0000 | 35 = (30+40)/2
  • 40.
    LAG – valuesfrom preceding (x) rows Copyright © 2019 Oracle and/or its affiliates. select a, lag(a,1) over w as 'lag(1)', lag(a,2) over w as 'lag(2)' from w1 window w as (order by a); +---+--------+--------+ | a | lag(1) | lag(2) | +---+--------+--------+ | 1 | NULL | NULL | | 2 | 1 | NULL | | 3 | 2 | 1 | | 4 | 3 | 2 | +---+--------+--------+
  • 41.
    LEAD – valuesfrom preceding (x) rows Copyright © 2019 Oracle and/or its affiliates. select a, lead(a,1) over w as 'lead(1)', lead(a,2) over w as 'lead(2)' from w1 window w as (order by a); +---+---------+---------+ | a | lead(1) | lead(2) | +---+---------+---------+ | 1 | 2 | 3 | | 2 | 3 | 4 | | 3 | 4 | NULL | | 4 | NULL | NULL |
  • 42.
    First and LastValues Copyright © 2019 Oracle and/or its affiliates. select a, FIRST_VALUE(a) over w as 'first value', LAST_VALUE(a) over w as 'last value' from w1 window w as (order by a); +---+-------------+------------+ | a | first value | last value | +---+-------------+------------+ | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 1 | 3 | | 4 | 1 | 4 |
  • 43.
    N Values Copyright ©2019 Oracle and/or its affiliates. select a, NTH_VALUE(a,2) over w as 'n2', NTH_VALUE(a,3) over w as 'n3' from w1 window w as (order by a); +---+------+------+ | a | n2 | n3 | +---+------+------+ | 1 | NULL | NULL | No 2nd or 3rd row read so no values | 2 | 2 | NULL | No 3rd row read so no values | 3 | 2 | 3 | | 4 | 2 | 3 |
  • 44.
    Another Example Copyright ©2019 Oracle and/or its affiliates. SELECT * FROM staff ORDER by id; +----+-------+-----------+---------+ | id | name | dept | salary | +----+-------+-----------+---------+ | 1 | Andy | Shipping | 5400.00 | | 2 | Betty | Marketing | 6300.00 | | 3 | Tracy | Shipping | 4800.00 | | 4 | Mike | Marketing | 7100.00 | | 5 | Sandy | Sales | 5400.00 | | 6 | James | Shipping | 6000.00 | | 7 | Carol | Sales | 4600.00 |
  • 45.
    Another Example Copyright ©2019 Oracle and/or its affiliates. SELECT COUNT(*), SUM(salary) FROM staff; +----------+-------------+ | COUNT(*) | SUM(salary) | +----------+-------------+ | 7 | 39600.00 | +----------+-------------+
  • 46.
    Another Example Copyright ©2019 Oracle and/or its affiliates. SELECT COUNT(*) AS 'nbr' , SUM(salary) as 'tot salary', ROUND(avg(salary),0) AS avg FROM staff GROUP BY dept ORDER BY dept; +-----+------------+------+ | nbr | tot salary | avg | +-----+------------+------+ | 2 | 13400.00 | 6700 | | 2 | 10000.00 | 5000 | | 3 | 16200.00 | 5400 |
  • 47.
    Another Example Copyright ©2019 Oracle and/or its affiliates. SELECT dept, COUNT(*) AS 'nbr' , SUM(salary) as 'tot salary', ROUND(avg(salary),0) AS avg FROM staff GROUP BY dept WITH ROLLUP order by dept; +-----------+-----+------------+------+ | dept | nbr | tot salary | avg | +-----------+-----+------------+------+ | NULL | 7 | 39600.00 | 5657 | | Marketing | 2 | 13400.00 | 6700 | | Sales | 2 | 10000.00 | 5000 | | Shipping | 3 | 16200.00 | 5400 |
  • 48.
    Another Example Copyright ©2019 Oracle and/or its affiliates. SELECT dept, COUNT(*) AS 'nbr' , SUM(salary) as 'tot salary', ROUND(avg(salary),0) AS avg FROM staff GROUP BY dept WITH ROLLUP order by dept; +-----------+-----+------------+------+ | dept | nbr | tot salary | avg | +-----------+-----+------------+------+ | NULL | 7 | 39600.00 | 5657 | | Marketing | 2 | 13400.00 | 6700 | | Sales | 2 | 10000.00 | 5000 | | Shipping | 3 | 16200.00 | 5400 |
  • 49.
    Calculate Rank Copyright ©2019 Oracle and/or its affiliates. SELECT name, salary, RANK() OVER s AS 'Rank' , DENSE_RANK() OVER s AS ‘Dense Rank' FROM staff WINDOW s as (ORDER BY salary DESC) ; +-------+---------+------+-------------+ | name | salary | Rank | Dense Rank | +-------+---------+------+-------------+ | Mike | 7100.00 | 1 | 1 | | Betty | 6300.00 | 2 | 2 | | James | 6000.00 | 3 | 3 | | Sandy | 5400.00 | 4 | 4 | | Andy | 5400.00 | 4 | 4 | | Tracy | 4800.00 | 6 | 5 | | Carol | 4600.00 | 7 | 6 |
  • 50.
    Percentage of TotalSalary Copyright © 2019 Oracle and/or its affiliates. SELECT name, salary, ROUND(salary / SUM(salary) OVER() * 100,2) AS '%' FROM staff ORDER BY salary DESC; +-------+---------+-------+ | name | salary | % | +-------+---------+-------+ | Mike | 7100.00 | 17.93 | | Betty | 6300.00 | 15.91 | | James | 6000.00 | 15.15 | | Andy | 5400.00 | 13.64 | | Sandy | 5400.00 | 13.64 | | Tracy | 4800.00 | 12.12 | | Carol | 4600.00 | 11.62 |
  • 51.
    Difference to AVGSalary Copyright © 2019 Oracle and/or its affiliates. SELECT name, salary, ROUND(AVG(salary) OVER (),2) AS 'avg', ROUND(salary - AVG(salary) OVER (),2) AS 'Diff to AVG' FROM staff ORDER BY salary DESC; +-------+---------+---------+-------------+ | name | salary | avg | Diff to AVG | +-------+---------+---------+-------------+ | Mike | 7100.00 | 5657.14 | 1442.86 | | Betty | 6300.00 | 5657.14 | 642.86 | | James | 6000.00 | 5657.14 | 342.86 | | Andy | 5400.00 | 5657.14 | -257.14 | | Sandy | 5400.00 | 5657.14 | -257.14 | | Tracy | 4800.00 | 5657.14 | -857.14 | | Carol | 4600.00 | 5657.14 | -1057.14 |
  • 52.
    Calculate differences insalary Copyright © 2019 Oracle and/or its affiliates. SELECT name, salary, salary - LEAD(salary,1) OVER (ORDER BY salary DESC) as 'diff next' FROM staff ORDER BY salary DESC; +-------+---------+-----------+ | name | salary | diff next | +-------+---------+-----------+ | Mike | 7100.00 | 800.00 | | Betty | 6300.00 | 300.00 | | James | 6000.00 | 600.00 | | Andy | 5400.00 | 0.00 | | Sandy | 5400.00 | 600.00 | | Tracy | 4800.00 | 200.00 | | Carol | 4600.00 | NULL |
  • 53.
    Calculate differences insalary Copyright © 2019 Oracle and/or its affiliates. SELECT name, salary, salary - LAST_VALUE(salary) OVER w AS 'above', ROUND((salary - LAST_VALUE(salary) OVER w) / LAST_VALUE(salary) OVER w * 100) AS 'pct above' FROM staff WINDOW w as (ORDER by salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) ORDER BY salary DESC; +-------+---------+---------+-----------+ | name | salary | above | pct above | +-------+---------+---------+-----------+ | Mike | 7100.00 | 2500.00 | 54 | | Betty | 6300.00 | 1700.00 | 37 | | James | 6000.00 | 1400.00 | 30 | | Sandy | 5400.00 | 800.00 | 17 | | Andy | 5400.00 | 800.00 | 17 | | Tracy | 4800.00 | 200.00 | 4 | | Carol | 4600.00 | 0.00 | 0 |
  • 54.
    Calculate differences insalary Copyright © 2019 Oracle and/or its affiliates. SELECT name, dept, salary, round(AVG(salary) OVER z,2) AS 'Avg', round(salary - AVG(salary) OVER z,2) as 'diff avg' FROM staff WINDOW z AS (PARTITION BY dept) ORDER BY dept, salary DESC; +-------+-----------+---------+---------+----------+ | name | dept | salary | Avg | diff avg | +-------+-----------+---------+---------+----------+ | Mike | Marketing | 7100.00 | 6700.00 | 400.00 | | Betty | Marketing | 6300.00 | 6700.00 | -400.00 | | Sandy | Sales | 5400.00 | 5000.00 | 400.00 | | Carol | Sales | 4600.00 | 5000.00 | -400.00 | | James | Shipping | 6000.00 | 5400.00 | 600.00 | | Andy | Shipping | 5400.00 | 5400.00 | 0.00 | | Tracy | Shipping | 4800.00 | 5400.00 | -600.00 |
  • 55.
    Company and DepartmentalRanks Copyright © 2019 Oracle and/or its affiliates. SELECT name, dept, salary, RANK() OVER s AS dept_rank, RANK() OVER (ORDER BY salary DESC) AS Company_rank FROM staff WINDOW s as (PARTITION BY dept ORDER BY salary DESC) ORDER BY dept, salary DESC; +-------+-----------+---------+-----------+--------------+ | name | dept | salary | dept_rank | Company_rank | +-------+-----------+---------+-----------+--------------+ | Mike | Marketing | 7100.00 | 1 | 1 | | Betty | Marketing | 6300.00 | 2 | 2 | | Sandy | Sales | 5400.00 | 1 | 4 | | Carol | Sales | 4600.00 | 2 | 7 | | James | Shipping | 6000.00 | 1 | 3 | | Andy | Shipping | 5400.00 | 2 | 4 | | Tracy | Shipping | 4800.00 | 3 | 6 |
  • 56.
    Good Place toStop .. for now. Copyright © 2019 Oracle and/or its affiliates.
  • 57.
    Excellent Tutorial onWindowing Functions https://momjian.us/main/writings/pgsql/window.pdf Copyright © 2019 Oracle and/or its affiliates.
  • 58.
  • 59.
    Please buy myBook! Copyright © 2019 Oracle and/or its affiliates.
  • 60.
    Q/A Copyright © 2019Oracle and/or its affiliates. Slides at Slideshare.net/davidmstokes Email at David.Stokes @ Oracle.com Blog at https://elephantdolphin.blogspot.com/