Oracle Database 11g SQL Parte 2
Oracle Database 11g SQL Parte 2
Oracle Database 11g SQL Parte 2
171
1hs vuue ol 26.22 s used n the HAVING cuuse ol the outer query to lter the group's rows
to those huvng un uveruge prce ess thun 26.22. 1he loowng query shows u verson ol the
outer query thut retreves the product_type_id und uveruge prce ol the products grouped
by product_type_id:
SELECT product_type_id, AVG(price)
FROM products
GROUP BY product_type_id
ORDER BY product_type_id;
PRODUCT_TYPE_ID AVG(PRICE)
--------------- ----------
1 24.975
2 26.22
3 13.24
4 13.99
13.49
1he groups wth u product_type_id ol l, 3, 4, und nu huve un uveruge prce ess thun
26.22. As expected, these ure the sume groups returned by the query ut the sturt ol ths secton.
Subqucrics in a fROM CIausc (lnIinc Vicws)
You muy puce u subquery n the FROM cuuse ol un outer query. 1hese types ol subqueres ure
uso known us , becuuse the subquery provdes dutu n ne wth the FROM cuuse.
1he loowng smpe exumpe retreves the products whose product_id s ess thun 3:
SELECT product_id
FROM
(SELECT product_id
FROM products
WHERE product_id < 3);
PRODUCT_ID
----------
1
2
Notce thut the subquery returns the rows lrom the products tube whose product_id s
ess thun 3 to the outer query, whch then retreves und dspuys those product_id vuues. As lur
us the FROM cuuse ol the outer query s concerned, the output lrom the subquery s ust unother
source ol dutu.
1he next exumpe s more uselu und retreves the product_id und price lrom the
products tube n the outer query, und the subquery retreves the number ol tmes u product
hus been purchused:
SELECT prds.product_id, price, purchases_data.product_count
FROM products prds,
(SELECT product_id, COUNT(product_id) product_count
172
Cruce Dutubuse ll SQL
FROM purchases
GROUP BY product_id) purchases_data
WHERE prds.product_id = purchases_data.product_id;
PRODUCT_ID PRICE PRODUCT_COUNT
---------- ---------- -------------
1 19.95 4
2 30 4
3 25.99 1
Notce thut the subquery retreves the product_id und COUNT(product_id) lrom the
purchases tube und returns them to the outer query. As you cun see, the output lrom subquery
s ust unother source ol dutu to the FROM cuuse ol the outer query.
frrurs Yuu Might fncuuntcr
ln ths secton, you' see some errors you mght encounter. Speclcuy, you' see thut u snge-
row subquery muy return u muxmum ol one row und thut u subquery muy not contun un ORDER
BY cuuse.
SingIc-Ruw Subqucrics May Rcturn a Maximum uf Onc Ruw
ll your subquery returns more thun one row, you' get the loowng error:
ORA-01427: single-row subquery returns more than one row.
lor exumpe, the subquery n the loowng stutement uttempts to puss mutpe rows to the
equuty operutor (=) n the outer query:
SQL> SELECT product_id, name
2 FROM products
3 WHERE product_id =
4 (SELECT product_id
5 FROM products
6 WHERE name LIKE '%e%');
(SELECT product_id
*
ERROR at line 4:
ORA-01427: single-row subquery returns more than one row
1here ure nne rows n the products tube whose numes contun the etter e, und the
subquery uttempts to puss these rows to the equuty operutor n the outer query. ecuuse the
equuty operutor cun hunde ony u snge row, the query s nvud und un error s returned.
You' eurn how to return mutpe rows lrom u subquery uter n the secton \rtng
Mutpe-Row Subqueres.
Subqucrics May Nut Cuntain an ORDfR BY CIausc
A subquery muy not contun un ORDER BY cuuse. lnsteud, uny orderng must be done n the
outer query. lor exumpe, the loowng outer query hus un ORDER BY cuuse ut the end thut sorts
the product_id vuues n descendng order:
Chupter 6: Subqueres
173
SELECT product_id, name, price
FROM products
WHERE price >
(SELECT AVG(price)
FROM products)
ORDER BY product_id DESC;
PRODUCT_ID NAME PRICE
---------- ------------------------------ ----------
5 Z Files 49.99
3 Supernova 25.99
2 Chemistry 30
1 Modern Science 19.95
Writing MuItipIc-Ruw Subqucrics
You use u mutpe-row subquery to return one or more rows to un outer SQL stutement. 1o
hunde u subquery thut returns mutpe rows, your outer query muy use the IN, ANY, or ALL
operutor. As you suw n Chupter 2, you cun use these operutors to see l u coumn vuue s
contuned n u st ol vuues, lor exumpe:
SELECT product_id, name
FROM products
WHERE product_id IN (1, 2, 3);
PRODUCT_ID NAME
---------- -------------------
1 Modern Science
2 Chemistry
3 Supernova
As you' see n ths secton, the st ol vuues cun come lrom u subquery.
NOTf
You can ao uc |hc EXISTS opcra|or |o chcc| | a vauc n a |
rc|urncd b, a corrca|cd ubqucr,. You' carn abou| |h a|cr, n |hc
cc|on Vr|ng Corrca|cd Subqucrc."
Using lN with a MuItipIc-Ruw Subqucry
As you suw n Chupter 2, you use IN to check l u vuue s n u specled st ol vuues. 1he st ol
vuues muy come lrom the resuts returned by u subquery. You cun uso use NOT IN to perlorm
the ogcu opposte ol IN: to check l u vuue s not n u specled st ol vuues.
1he loowng smpe exumpe uses IN to check l u product_id s n the st ol vuues
returned by the subquery, the subquery returns the product_id lor products whose nume
contuns the etter e:
SELECT product_id, name
FROM products
WHERE product_id IN
174
Cruce Dutubuse ll SQL
(SELECT product_id
FROM products
WHERE name LIKE '%e%');
PRODUCT_ID NAME
---------- -------------------
1 Modern Science
2 Chemistry
3 Supernova
5 Z Files
6 2412: The Return
7 Space Force 9
8 From Another Planet
11 Creative Yell
12 My Front Line
1he next exumpe uses NOT IN to get the products thut ure not n the purchases tube:
SELECT product_id, name
FROM products
WHERE product_id NOT IN
(SELECT product_id
FROM purchases);
PRODUCT_ID NAME
---------- -------------------
4 Tank War
5 Z Files
6 2412: The Return
7 Space Force 9
8 From Another Planet
9 Classical Music
10 Pop 3
11 Creative Yell
12 My Front Line
Using ANY with a MuItipIc-Ruw Subqucry
You use the ANY operutor to compure u vuue wth uny vuue n u st. You must puce un =, <>,
<, >, <=, or >= operutor belore ANY n your query. 1he loowng exumpe uses ANY to get the
empoyees whose suury s ess thun uny ol the owest suures n the salary_grades tube:
SELECT employee_id, last_name
FROM employees
WHERE salary < ANY
(SELECT low_salary
FROM salary_grades);
EMPLOYEE_ID LAST_NAME
----------- ----------
2 Johnson
3 Hobbs
4 Jones
Chupter 6: Subqueres
17S
Using All with a MuItipIc-Ruw Subqucry
You use the ALL operutor to compure u vuue wth uny vuue n u st. You must puce un =, <>,
<, >, <=, or >= operutor belore ALL n your query. 1he loowng exumpe uses ALL to get the
empoyees whose suury s greuter thun u ol the hghest suures n the salary_grades tube:
SELECT employee_id, last_name
FROM employees
WHERE salary > ALL
(SELECT high_salary
FROM salary_grades);
no rows selected
As you cun see, no empoyee hus u suury greuter thun the hghest suury.
Writing MuItipIc-CuIumn Subqucrics
1he subqueres you've seen so lur huve returned rows contunng one coumn. You're not mted
to one coumn: you cun wrte subqueres thut return mutpe coumns. 1he loowng exumpe
retreves the products wth the owest prce lor euch product type group:
SELECT product_id, product_type_id, name, price
FROM products
WHERE (product_type_id, price) IN
(SELECT product_type_id, MIN(price)
FROM products
GROUP BY product_type_id);
PRODUCT_ID PRODUCT_TYPE_ID NAME PRICE
---------- --------------- ------------------------------ ----------
1 1 Modern Science 19.95
4 2 Tank War 13.95
8 3 From Another Planet 12.99
9 4 Classical Music 10.99
Notce thut the subquery returns the product_type_id und the mnmum price lor euch
group ol products, und these ure compured n the outer query's WHERE cuuse wth the product_
type_id und price lor euch product.
Writing CurrcIatcd Subqucrics
A correuted subquery relerences one or more coumns n the outer SQL stutement. 1hese ure
cued corrca|cd subqueres, becuuse they ure reuted to the outer SQL stutement through the
sume coumns.
You typcuy use u correuted subquery when you need un unswer to u queston thut depends on
u vuue n euch row contuned n un outer query. lor exumpe, you mght wunt to see whether there
s u reutonshp between the dutu, but you don't cure how muny rows ure returned by the subquery,
thut s, you ust wunt to check whether an, rows ure returned, but you don't cure how muny.
A correuted subquery s run once lor euch row n the outer query, ths s dllerent lrom u non-
correuted subquery, whch s run once pror to runnng the outer query. ln uddton, u correuted
176
Cruce Dutubuse ll SQL
subquery cun resove nu vuues. You' see exumpes n the loowng sectons thut ustrute
these concepts.
A CurrcIatcd Subqucry fxampIc
1he loowng correuted subquery retreves the products thut huve u prce greuter thun the
uveruge lor ther product type:
SELECT product_id, product_type_id, name, price
FROM products outer
WHERE price >
(SELECT AVG(price)
FROM products inner
WHERE inner.product_type_id = outer.product_type_id);
PRODUCT_ID PRODUCT_TYPE_ID NAME PRICE
---------- --------------- ------------------------------ ----------
2 1 Chemistry 30
5 2 Z Files 49.99
7 3 Space Force 9 13.49
10 4 Pop 3 15.99
11 4 Creative Yell 14.99
Notce thut l've used the uus outer to ube the outer query und the uus inner lor the
nner subquery. 1he relerence to the product_type_id coumn n both the nner und outer
purts s whut mukes the nner subquery correuted wth the outer query. Aso, the subquery returns
u snge row contunng the uveruge prce lor the product.
ln u correuted subquery, euch row n the outer query s pussed one ut u tme to the subquery.
1he subquery reuds euch row n turn lrom the outer query und uppes t to the subquery unt u
the rows lrom the outer query huve been processed. 1he resuts lrom the entre query ure then
returned.
ln the prevous exumpe, the outer query retreves euch row lrom the products tube
und pusses them to the nner query. Luch row s reud by the nner query, whch cucuutes the
uveruge prce lor euch product where the product_type_id n the nner query s equu to
the product_type_id n the outer query.
Using fXlSTS and NOT fXlSTS with a CurrcIatcd Subqucry
You use the EXISTS operutor to check lor the exstence ol rows returned by u subquery. Athough
you cun use EXISTS wth non-correuted subqueres, you' typcuy use t wth correuted
subqueres. 1he NOT EXISTS operutor does the ogcu opposte ol EXISTS: t checks l rows
do not exst n the resuts returned by u subquery.
Using fXlSTS with a CurrcIatcd Subqucry
1he loowng exumpe uses EXISTS to retreve empoyees who munuge other empoyees, notce
thut l don't cure how muny rows ure returned by the subquery, l ony cure whether uny rows ure
returned ut u:
SELECT employee_id, last_name
FROM employees outer
WHERE EXISTS
(SELECT employee_id
Chupter 6: Subqueres
177
FROM employees inner
WHERE inner.manager_id = outer.employee_id);
EMPLOYEE_ID LAST_NAME
----------- ----------
1 Smith
2 Johnson
ecuuse EXISTS ust checks lor the exstence ol rows returned by the subquery, u subquery
doesn't huve to return u coumnt cun ust return u teru vuue. 1hs leuture cun mprove the
perlormunce ol your query. lor exumpe, the loowng query rewrtes the prevous exumpe wth
the subquery returnng the teru vuue l:
SELECT employee_id, last_name
FROM employees outer
WHERE EXISTS
(SELECT 1
FROM employees inner
WHERE inner.manager_id = outer.employee_id);
EMPLOYEE_ID LAST_NAME
----------- ----------
1 Smith
2 Johnson
As ong us the subquery returns one or more rows, EXISTS returns true, l the subquery returns
no rows, EXISTS returns luse. ln the exumpes, l ddn't cure how muny rows ure returned by the
subquery: A l cured ubout wus whether uny rows (or no rows) ure returned, so thut EXISTS returns
true (or luse). ecuuse the outer query requres ut eust one coumn, the teru vuue l s returned
by the subquery n the prevous exumpe.
Using NOT fXlSTS with a CurrcIatcd Subqucry
1he loowng exumpe uses NOT EXISTS to retreve products thut huven't been purchused:
SELECT product_id, name
FROM products outer
WHERE NOT EXISTS
(SELECT 1
FROM purchases inner
WHERE inner.product_id = outer.product_id);
PRODUCT_ID NAME
---------- -------------------
4 Tank War
5 Z Files
6 2412: The Return
7 Space Force 9
8 From Another Planet
9 Classical Music
10 Pop 3
11 Creative Yell
12 My Front Line
178
Cruce Dutubuse llg SQL
fXlSTS and NOT fXlSTS Vcrsus lN and NOT lN
Lurer n the secton Lsng lN wth u Mutpe-Row Subquery, you suw how the IN operutor s
used to check l u vuue s contuned n u st. EXISTS s dllerent lrom IN: EXISTS checks ust
lor the exstence ol rows, whereus IN checks lor uctuu vuues.
TlP
EXISTS |,pca, o||cr bc||cr pcr|ormancc |han IN v|h ubqucrc.
Thcrc|orc, ,ou houd uc EXISTS ra|hcr |han IN vhcrcvcr pobc.
You shoud be curelu when wrtng queres thut use NOT EXISTS or NOT IN. \hen u st
ol vuues contuns u nu vuue, NOT EXISTS returns true, but NOT IN returns luse. Consder the
loowng exumpe thut uses NOT EXISTS und retreves the product types thut don't huve uny
products ol thut type n the products tube:
SELECT product_type_id, name
FROM product_types outer
WHERE NOT EXISTS
(SELECT 1
FROM products inner
WHERE inner.product_type_id = outer.product_type_id);
PRODUCT_TYPE_ID NAME
--------------- ----------
5 Magazine
Notce one row s returned by ths exumpe. 1he next exumpe rewrtes the prevous query to
use NOT IN, notce thut no rows ure returned:
SELECT product_type_id, name
FROM product_types
WHERE product_type_id NOT IN
(SELECT product_type_id
FROM products);
no rows selected
No rows ure returned becuuse the subquery returns u st ol product_id vuues, one ol
whch s nu (the product_type_id lor product rl2 s nu). ecuuse ol ths, NOT IN n the
outer query returns luse, und therelore no rows ure returned. You cun get uround ths by usng the
NVL() luncton to convert nus to u vuue. ln the loowng exumpe, NVL() s used to convert
nu product_type_id vuues to 0:
SELECT product_type_id, name
FROM product_types
WHERE product_type_id NOT IN
(SELECT NVL(product_type_id, 0)
FROM products);
PRODUCT_TYPE_ID NAME
--------------- ----------
5 Magazine
1hs tme the row uppeurs.
Chupter 6: Subqueres
179
1hese exumpes ustrute unother dllerence between correuted und non-correuted
subqueres: u correuted query cun resove nu vuues.
Writing Ncstcd Subqucrics
You cun nest subqueres nsde other subqueres to u depth ol 255. You shoud use ths technque
spurngyyou muy lnd your query perlorms better usng tube ons. 1he loowng exumpe
contuns u nested subquery, notce thut t s contuned wthn u subquery, whch s tsel contuned
n un outer query:
SELECT product_type_id, AVG(price)
FROM products
GROUP BY product_type_id
HAVING AVG(price) <
(SELECT MAX(AVG(price))
FROM products
WHERE product_type_id IN
(SELECT product_id
FROM purchases
WHERE quantity > 1)
GROUP BY product_type_id)
ORDER BY product_type_id;
PRODUCT_TYPE_ID AVG(PRICE)
--------------- ----------
1 24.975
3 13.24
4 13.99
13.49
As you cun see, ths exumpe s qute compex und contuns three queres: u nested subquery,
u subquery, und the outer query. 1hese query purts ure run n thut order. Let's breuk the exumpe
down nto the three purts und exumne the resuts returned. 1he nested subquery s
SELECT product_id
FROM purchases
WHERE quantity > 1
1hs subquery returns the product_id lor the products thut huve been purchused more thun
once. 1he rows returned by ths subquery ure
PRODUCT_ID
----------
2
1
1he subquery thut receves ths output s
SELECT MAX(AVG(price))
FROM products
WHERE product_type_id IN
(... output from the nested subquery ...)
GROUP BY product_type_id
180
Cruce Dutubuse ll SQL
1hs subquery returns the muxmum uveruge prce lor the products returned by the nested
subquery. 1he row returned s
MAX(AVG(PRICE))
---------------
26.22
1hs row s returned to the loowng outer query:
SELECT product_type_id, AVG(price)
FROM products
GROUP BY product_type_id
HAVING AVG(price) <
(... output from the subquery ...)
ORDER BY product_type_id;
1hs query returns the product_type_id und uveruge prce ol products thut ure ess thun
uveruge returned by the subquery. 1he rows returned ure
PRODUCT_TYPE_ID AVG(PRICE)
--------------- ----------
1 24.975
3 13.24
4 13.99
13.49
1hese ure the rows returned by the compete query shown ut the sturt ol ths secton.
Writing UPDATf and DflfTf Statcmcnts
Cuntaining Subqucrics
So lur, you've ony seen subqueres contuned n u SELECT stutement. As you' see n ths
secton, you cun uso put subqueres nsde UPDATE und DELETE stutements.
Writing an UPDATf Statcmcnt Cuntaining a Subqucry
ln un UPDATE stutement, you cun set u coumn to the resut returned by u snge-row subquery.
lor exumpe, the loowng UPDATE stutement sets empoyee r4's suury to the uveruge ol the hgh
suury grudes returned by u subquery:
UPDATE employees
SET salary =
(SELECT AVG(high_salary)
FROM salary_grades)
WHERE employee_id = 4;
1 row updated.
Dong ths ncreuses empoyee r4's suury lrom S500,000 to S625,000 (ths s the uveruge ol
the hgh suures lrom the salary_grades tube).
Chupter 6: Subqueres
181
NOTf
| ,ou cxccu|c |hc UPDATE |a|cmcn|, rcmcmbcr |o cxccu|c a
ROLLBACK |o undo |hc changc. Tha| va,, ,our rcu| v ma|ch
|hoc hovn a|cr n |h boo|.
Writing a DflfTf Statcmcnt Cuntaining a Subqucry
You cun use the rows returned by u subquery n the WHERE cuuse ol u DELETE stutement. lor
exumpe, the loowng DELETE stutement removes the empoyee whose suury s greuter thun
the uveruge ol the hgh suury grudes returned by u subquery:
DELETE FROM employees
WHERE salary >
(SELECT AVG(high_salary)
FROM salary_grades);
1 row deleted.
1hs DELETE stutement removes empoyee rl.
NOTf
| ,ou cxccu|c |hc DELETE |a|cmcn|, rcmcmbcr |o cxccu|c a
ROLLBACK |o undo |hc rcmova o| |hc rov.
Summary
ln ths chupter, you eurned the loowng:
A subquery s u query puced wthn u SELECT, UPDATE, or DELETE stutement.
Snge-row subqueres return zero or one row.
Mutpe-row subqueres return one or more rows.
Mutpe-coumn subqueres return more thun one coumn.
Correuted subqueres relerence one or more coumns n the outer SQL stutement.
Nested subqueres ure subqueres puced wthn unother subquery.
ln the next chupter, you' eurn ubout udvunced queres.
This page intentionally left blank
Advunced Queres
l83
184
Cruce Dutubuse llg SQL
n ths chupter, you w see how to
Lse the set operutors, whch uow you to combne rows returned by two or more queres.
Lse the TRANSLATE() luncton to trunsute churucters n one strng to churucters n
unother strng.
Lse the DECODE() luncton to seurch lor u certun vuue n u set ol vuues.
Lse the CASE expresson to perlorm l-then-ese ogc n SQL.
lerlorm queres on herurchcu dutu.
Lse the ROLLUP und CUBE cuuses to get subtotus und totus lor groups ol rows.
1uke udvuntuge ol the unuytc lunctons, whch perlorm compex cucuutons, such us
lndng the top-seng product type lor euch month, the top suespersons, und so on.
lerlorm nter-row cucuutons wth the MODEL cuuse.
Lse the new Cruce Dutubuse llg PIVOT und UNPIVOT cuuses, whch ure uselu lor
seeng overu trends n urge umounts ol dutu.
Let's punge n und exumne the set operutors.
Using thc Sct Opcraturs
1he set operutors uow you to combne rows returned by two or more queres. 1ube 7-l shows
the lour set operutors.
You must keep n mnd the loowng restrcton when usng u set operutor: Thc numbcr o|
coumn and |hc coumn |,pc rc|urncd b, |hc qucrc mu| ma|ch, a|hough |hc coumn namc
ma, bc d||crcn|.
You' eurn how to use euch ol the set operutors shown n 1ube 7-l shorty, but lrst et's ook
ut the exumpe tubes used n ths secton.
Opcratur Dcscriptiun
UNION ALL
Returns u the rows retreved by the queres, ncudng dupcute rows.
UNION
Returns u non-dupcute rows retreved by the queres.
INTERSECT
Returns rows thut ure retreved by both queres.
MINUS
Returns the remunng rows when the rows retreved by the second
query ure subtructed lrom the rows retreved by the lrst query.
TABlf 7-1 Sc| Cpcra|or
Chupter 7: Advunced Queres
18S
Thc fxampIc TabIcs
1he products und more_products tubes ure creuted by the store_schema.sql scrpt
usng the loowng stutements:
CREATE TABLE products (
product_id INTEGER
CONSTRAINT products_pk PRIMARY KEY,
product_type_id INTEGER
CONSTRAINT products_fk_product_types
REFERENCES product_types(product_type_id),
name VARCHAR2(30) NOT NULL,
description VARCHAR2(50),
price NUMBER(5, 2)
);
CREATE TABLE more_products (
prd_id INTEGER
CONSTRAINT more_products_pk PRIMARY KEY,
prd_type_id INTEGER
CONSTRAINT more_products_fk_product_types
REFERENCES product_types(product_type_id),
name VARCHAR2(30) NOT NULL,
available CHAR(1)
);
1he loowng query retreves the product_id, product_type_id, und name coumns
lrom the products tube:
SELECT product_id, product_type_id, name
FROM products;
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- -------------------
1 1 Modern Science
2 1 Chemistry
3 2 Supernova
4 2 Tank War
5 2 Z Files
6 2 2412: The Return
7 3 Space Force 9
8 3 From Another Planet
9 4 Classical Music
10 4 Pop 3
11 4 Creative Yell
12 My Front Line
1he next query retreves the prd_id, prd_type_id, und name coumns lrom the more_
products tube:
SELECT prd_id, prd_type_id, name
FROM more_products;
186
Cruce Dutubuse ll SQL
PRD_ID PRD_TYPE_ID NAME
---------- ----------- --------------
1 1 Modern Science
2 1 Chemistry
3 Supernova
4 2 Lunar Landing
5 2 Submarine
Using thc UNlON All Opcratur
1he UNION ALL operutor returns u the rows retreved by the queres, ncudng dupcute rows.
1he loowng query uses UNION ALL, notce thut u the rows lrom products und more_
products ure retreved, ncudng dupcutes:
SELECT product_id, product_type_id, name
FROM products
UNION ALL
SELECT prd_id, prd_type_id, name
FROM more_products;
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- ------------------------------
1 1 Modern Science
2 1 Chemistry
3 2 Supernova
4 2 Tank War
5 2 Z Files
6 2 2412: The Return
7 3 Space Force 9
8 3 From Another Planet
9 4 Classical Music
10 4 Pop 3
11 4 Creative Yell
12 My Front Line
1 1 Modern Science
2 1 Chemistry
3 Supernova
4 2 Lunar Landing
5 2 Submarine
17 rows selected.
You cun sort the rows usng the ORDER BY cuuse loowed by the poston ol the coumn.
1he loowng exumpe uses ORDER BY 1 to sort the rows by the lrst coumn retreved by the
two queres (product_id und prd_id):
SELECT product_id, product_type_id, name
FROM products
UNION ALL
SELECT prd_id, prd_type_id, name
FROM more_products
ORDER BY 1;
Chupter 7: Advunced Queres
187
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- -------------------
1 1 Modern Science
1 1 Modern Science
2 1 Chemistry
2 1 Chemistry
3 2 Supernova
3 Supernova
4 2 Tank War
4 2 Lunar Landing
5 2 Z Files
5 2 Submarine
6 2 2412: The Return
7 3 Space Force 9
8 3 From Another Planet
9 4 Classical Music
10 4 Pop 3
11 4 Creative Yell
12 My Front Line
17 rows selected.
Using thc UNlON Opcratur
1he UNION operutor returns ony the non-dupcute rows retreved by the queres. 1he loowng
exumpe uses UNION, notce the dupcute Modern Scence und Chemstry rows ure not
retreved, und so ony l5 rows ure returned:
SELECT product_id, product_type_id, name
FROM products
UNION
SELECT prd_id, prd_type_id, name
FROM more_products;
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- -------------------
1 1 Modern Science
2 1 Chemistry
3 2 Supernova
3 Supernova
4 2 Lunar Landing
4 2 Tank War
5 2 Submarine
5 2 Z Files
6 2 2412: The Return
7 3 Space Force 9
8 3 From Another Planet
9 4 Classical Music
10 4 Pop 3
11 4 Creative Yell
12 My Front Line
15 rows selected.
188
Cruce Dutubuse ll SQL
Using thc lNTfRSfCT Opcratur
1he INTERSECT operutor returns ony rows thut ure retreved by both queres. 1he loowng
exumpe uses INTERSECT, notce thut the Modern Scence und Chemstry rows ure returned:
SELECT product_id, product_type_id, name
FROM products
INTERSECT
SELECT prd_id, prd_type_id, name
FROM more_products;
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- --------------
1 1 Modern Science
2 1 Chemistry
Using thc MlNUS Opcratur
1he MINUS operutor returns the remunng rows when the rows retreved by the second query ure
subtructed lrom the rows retreved by the lrst query. 1he loowng exumpe uses MINUS, notce
thut the rows lrom more_products ure subtructed lrom products und the remunng rows ure
returned:
SELECT product_id, product_type_id, name
FROM products
MINUS
SELECT prd_id, prd_type_id, name
FROM more_products;
PRODUCT_ID PRODUCT_TYPE_ID NAME
---------- --------------- -------------------
3 2 Supernova
4 2 Tank War
5 2 Z Files
6 2 2412: The Return
7 3 Space Force 9
8 3 From Another Planet
9 4 Classical Music
10 4 Pop 3
11 4 Creative Yell
12 My Front Line
10 rows selected.
Cumbining Sct Opcraturs
You cun combne more thun two queres wth mutpe set operutors, wth the returned resuts
lrom one operutor leedng nto the next operutor. y deluut, set operutors ure evuuuted lrom
top to bottom, but you shoud ndcute the order usng purentheses n cuse Cruce Corporuton
chunges ths deluut behuvor n luture soltwure reeuses.
ln the exumpes n ths secton, l' use the loowng product_changes tube (creuted by
the store_schema.sql scrpt):
Chupter 7: Advunced Queres
where
condition1, condition2, ..., conditionN ure the expressons to be evuuuted.
result1, result2, ..., resultN ure the returned resuts (one lor euch possbe
condton). ll condition1 s true, result1 s returned, und smury lor the other
expressons.
default_result s returned when there s no condton thut returns true.
1he loowng exumpe ustrutes the use ol u seurched CASE expresson:
SELECT product_id, product_type_id,
CASE
WHEN product_type_id = 1 THEN 'Book'
WHEN product_type_id = 2 THEN 'Video'
WHEN product_type_id = 3 THEN 'DVD'
WHEN product_type_id = 4 THEN 'CD'
ELSE 'Magazine'
END
FROM products;
PRODUCT_ID PRODUCT_TYPE_ID CASEPROD
---------- --------------- --------
1 1 Book
2 1 Book
3 2 Video
4 2 Video
5 2 Video
6 2 Video
7 3 DVD
8 3 DVD
9 4 CD
10 4 CD
11 4 CD
12 Magazine
You cun use operutors n u seurched CASE expresson, us shown n the loowng exumpe:
SELECT product_id, price,
CASE
WHEN price > 15 THEN 'Expensive'
ELSE 'Cheap'
END
FROM products;
PRODUCT_ID PRICE CASEWHENP
---------- ---------- ---------
1 19.95 Expensive
2 30 Expensive
3 25.99 Expensive
4 13.95 Cheap
5 49.99 Expensive
196
Cruce Dutubuse ll SQL
6 14.95 Cheap
7 13.49 Cheap
8 12.99 Cheap
9 10.99 Cheap
10 15.99 Expensive
11 14.99 Cheap
12 13.49 Cheap
You w see more udvunced exumpes ol CASE expressons uter n ths chupter und n
Chupter l6.
HicrarchicaI Qucrics
You' qute olten encounter dutu thut s orgunzed n u herurchcu munner. Lxumpes ncude
the peope who work n u compuny, u lumy tree, und the purts thut muke up un engne. ln ths
secton, you' see queres thut uccess u herurchy ol empoyees who work lor our mugnury store.
Thc fxampIc Data
You' see the use ol u tube numed more_employees, whch s creuted by the store_
schema.sql scrpt us loows:
CREATE TABLE more_employees (
employee_id INTEGER
CONSTRAINT more_employees_pk PRIMARY KEY,
manager_id INTEGER
CONSTRAINT more_empl_fk_fk_more_empl
REFERENCES more_employees(employee_id),
first_name VARCHAR2(10) NOT NULL,
last_name VARCHAR2(10) NOT NULL,
title VARCHAR2(20),
salary NUMBER(6, 0)
);
1he manager_id coumn s u sel-relerence buck to the employee_id coumn ol the
more_employees tube, manager_id ndcutes the munuger ol un empoyee (l uny). 1he
loowng query returns the rows lrom more_employees:
SELECT *
FROM more_employees;
EMPLOYEE_ID MANAGER_ID FIRST_NAME LAST_NAME TITLE SALARY
----------- ---------- ---------- ---------- ------------- ----------
1 James Smith CEO 800000
2 1 Ron Johnson Sales Manager 600000
3 2 Fred Hobbs Sales Person 200000
4 1 Susan Jones Support Manager 500000
5 2 Rob Green Sales Person 40000
6 4 Jane Brown Support Person 45000
7 4 John Grey Support Manager 30000
8 7 Jean Blue Support Person 29000
Chupter 7: Advunced Queres
197
9 6 Henry Heyson Support Person 30000
10 1 Kevin Black Ops Manager 100000
11 10 Keith Long Ops Person 50000
12 10 Frank Howard Ops Person 45000
13 10 Doreen Penn Ops Person 47000
As you cun see, t's dllcut to pck out the empoyee reutonshps lrom ths dutu. lgure 7-l
shows the reutonshps n u gruphcu lorm.
As you cun see lrom lgure 7-l, the eementsor nodclorm u tree. 1rees ol nodes huve the
loowng techncu terms ussocuted wth them:
Ruut nudc 1he root s the node ut the top ol the tree. ln the exumpe shown n
lgure 7-l, the root node s }umes Smth, the CLC.
Parcnt nudc A purent s u node thut hus one or more nodes beneuth t. lor exumpe,
}umes Smth s the purent to the loowng nodes: Ron }ohnson, Susun }ones, und
Kevn uck.
ChiId nudc A chd s u node thut hus one purent node ubove t. lor exumpe,
Ron }ohnson's purent s }umes Smth.
lcaf nudc A eul s u node thut hus no chdren. lor exumpe, lred Hobbs und
Rob Creen ure eul nodes.
You use the CONNECT BY und START WITH cuuses ol u SELECT stutement to perlorm
herurchcu queres, us descrbed next.
flGURf 7-1 mpo,cc rca|onhp
198
Cruce Dutubuse ll SQL
Using thc CONNfCT BY and START WlTH CIauscs
1he syntux lor the CONNECT BY und START WITH cuuses ol u SELECT stutement s
SELECT [LEVEL], column, expression, ...
FROM table
[WHERE where_clause]
[[START WITH start_condition] [CONNECT BY PRIOR prior_condition]];
where
LEVEL s u pseudo coumn thut tes you how lur nto u tree you ure. LEVEL returns l lor
u root node, 2 lor u chd ol the root, und so on.
start_condition specles where to sturt the herurchcu query. You must specly u
START WITH cuuse when wrtng u herurchcu query. An exumpe start_condition
s employee_id = 1, whch specles the query sturts lrom empoyee rl.
prior_condition specles the reutonshp between the purent und chd rows. You
must specly u CONNECT BY PRIOR cuuse when wrtng u herurchcu query. An
exumpe prior_condition s employee_id = manager_id, whch specles the
reutonshp s between the purent employee_id und the chd manager_idthut s,
the chd's manager_id ponts to the purent's employee_id.
1he loowng query ustrutes the use ol the START WITH und CONNECT BY PRIOR
cuuses, notce thut the lrst row contuns the detus ol }umes Smth (empoyee rl), the second
row contuns the detus ol Ron }ohnson, whose manager_id s l, und so on:
SELECT employee_id, manager_id, first_name, last_name
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id;
EMPLOYEE_ID MANAGER_ID FIRST_NAME LAST_NAME
----------- ---------- ---------- ---------
1 James Smith
2 1 Ron Johnson
3 2 Fred Hobbs
5 2 Rob Green
4 1 Susan Jones
6 4 Jane Brown
9 6 Henry Heyson
7 4 John Grey
8 7 Jean Blue
10 1 Kevin Black
11 10 Keith Long
12 10 Frank Howard
13 10 Doreen Penn
Using thc lfVfl Pscudu CuIumn
1he next query ustrutes the use ol the LEVEL pseudo coumn to dspuy the eve n the tree:
Chupter 7: Advunced Queres
199
SELECT LEVEL, employee_id, manager_id, first_name, last_name
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id
ORDER BY LEVEL;
LEVEL EMPLOYEE_ID MANAGER_ID FIRST_NAME LAST_NAME
---------- ----------- ---------- ---------- ---------
1 1 James Smith
2 2 1 Ron Johnson
2 4 1 Susan Jones
2 10 1 Kevin Black
3 3 2 Fred Hobbs
3 7 4 John Grey
3 12 10 Frank Howard
3 13 10 Doreen Penn
3 11 10 Keith Long
3 5 2 Rob Green
3 6 4 Jane Brown
4 9 6 Henry Heyson
4 8 7 Jean Blue
1he next query uses the COUNT() luncton und LEVEL to get the number ol eves n the tree:
SELECT COUNT(DISTINCT LEVEL)
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id;
COUNT(DISTINCTLEVEL)
--------------------
4
furmatting thc RcsuIts frum a HicrarchicaI Qucry
You cun lormut the resuts lrom u herurchcu query usng LEVEL und the LPAD() luncton,
whch elt-puds vuues wth churucters. 1he loowng query uses LPAD(' ', 2 * LEVEL -
1) to elt-pud u totu ol 2 * LEVEL - 1 spuces, the resut ndents un empoyee's nume wth
spuces bused on ther LEVEL (thut s, LEVEL 1 sn't pudded, LEVEL 2 s pudded by two spuces,
LEVEL 3 by lour spuces, und so on):
SET PAGESIZE 999
COLUMN employee FORMAT A25
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id;
LEVEL EMPLOYEE
---------- ------------------
1 James Smith
2 Ron Johnson
200
Cruce Dutubuse ll SQL
3 Fred Hobbs
3 Rob Green
2 Susan Jones
3 Jane Brown
4 Henry Heyson
3 John Grey
4 Jean Blue
2 Kevin Black
3 Keith Long
3 Frank Howard
3 Doreen Penn
1he empoyee reutonshps ure eusy to pck out lrom these resuts.
Starting at a Nudc Othcr than thc Ruut
You don't huve to sturt ut the root node when truversng u tree: you cun sturt ut uny node usng the
START WITH cuuse. 1he loowng query sturts wth Susun }ones, notce thut LEVEL returns l lor
Susun }ones, 2 lor }une rown, und so on:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
START WITH last_name = 'Jones'
CONNECT BY PRIOR employee_id = manager_id;
LEVEL EMPLOYEE
---------- -----------------
1 Susan Jones
2 Jane Brown
3 Henry Heyson
2 John Grey
3 Jean Blue
ll the store hud more thun one empoyee wth the sume nume, you coud smpy use the
employee_id n the query's START WITH cuuse. lor exumpe, the loowng query uses Susun
}ones' employee_id ol 4:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
START WITH employee_id = 4
CONNECT BY PRIOR employee_id = manager_id;
1hs query returns the sume rows us the prevous one.
Using a Subqucry in a START WlTH CIausc
You cun use u subquery n u START WITH cuuse. lor exumpe, the loowng query uses u
subquery to seect the employee_id whose nume s Kevn uck, ths employee_id s pussed
to the START WITH cuuse:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
Chupter 7: Advunced Queres
201
FROM more_employees
START WITH employee_id = (
SELECT employee_id
FROM more_employees
WHERE first_name = 'Kevin'
AND last_name = 'Black'
)
CONNECT BY PRIOR employee_id = manager_id;
LEVEL EMPLOYEE
---------- ---------------
1 Kevin Black
2 Keith Long
2 Frank Howard
2 Doreen Penn
Travcrsing Upward Thruugh thc Trcc
You don't huve to truverse u tree downwurd lrom purents to chdren: you cun sturt ut u chd und
truverse upwurd. You do ths by swtchng chd und purent coumns n the CONNECT BY PRIOR
cuuse. lor exumpe, CONNECT BY PRIOR manager_id = employee_id connects the
chd's manager_id to the purent's employee_id.
1he loowng query sturts wth }eun ue und truverses upwurd u the wuy to }umes Smth,
notce thut LEVEL returns l lor }eun ue, 2 lor }ohn Crey, und so on:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
START WITH last_name = 'Blue'
CONNECT BY PRIOR manager_id = employee_id;
LEVEL EMPLOYEE
---------- ------------------
1 Jean Blue
2 John Grey
3 Susan Jones
4 James Smith
fIiminating Nudcs and Branchcs frum a HicrarchicaI Qucry
You cun emnute u purtcuur node lrom u query tree usng u WHERE cuuse. 1he loowng query
emnutes Ron }ohnson lrom the resuts usng WHERE last_name != 'Johnson':
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
WHERE last_name != 'Johnson'
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id;
LEVEL EMPLOYEE
---------- ------------------
1 James Smith
3 Fred Hobbs
202
Cruce Dutubuse ll SQL
3 Rob Green
2 Susan Jones
3 Jane Brown
4 Henry Heyson
3 John Grey
4 Jean Blue
2 Kevin Black
3 Keith Long
3 Frank Howard
3 Doreen Penn
You' notce thut uthough Ron }ohnson s emnuted lrom the resuts, hs empoyees lred
Hobbs und Rob Creen ure st ncuded. 1o emnute un entre brunch ol nodes lrom the resuts
ol u query, you udd un AND cuuse to your CONNECT BY PRIOR cuuse. lor exumpe, the
loowng query uses AND last_name != 'Johnson' to emnute Ron }ohnson und u hs
empoyees lrom the resuts:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id
AND last_name != 'Johnson';
LEVEL EMPLOYEE
---------- -------------------
1 James Smith
2 Susan Jones
3 Jane Brown
4 Henry Heyson
3 John Grey
4 Jean Blue
2 Kevin Black
3 Keith Long
3 Frank Howard
3 Doreen Penn
lncIuding Othcr Cunditiuns in a HicrarchicaI Qucry
You cun ncude other condtons n u herurchcu query usng u WHERE cuuse. 1he loowng
exumpe uses u WHERE cuuse to show ony empoyees whose suures ure ess thun or equu
to S50,000:
SELECT LEVEL,
LPAD(' ', 2 * LEVEL - 1) || first_name || ' ' || last_name AS employee,
salary
FROM more_employees
WHERE salary <= 50000
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id;
Chupter 7: Advunced Queres
203
LEVEL EMPLOYEE SALARY
---------- ------------------------- ----------
3 Rob Green 40000
3 Jane Brown 45000
4 Henry Heyson 30000
3 John Grey 30000
4 Jean Blue 29000
3 Keith Long 50000
3 Frank Howard 45000
3 Doreen Penn 47000
1hs concudes the dscusson ol herurchcu queres. ln the next secton, you' eurn ubout
udvunced group cuuses.
Using thc fxtcndcd GROUP BY CIauscs
ln ths secton, you' eurn ubout
ROLLUP, whch extends the GROUP BY cuuse to return u row contunng u subtotu lor
euch group ol rows, pus u row contunng u grund totu lor u the groups.
CUBE, whch extends the GROUP BY cuuse to return rows contunng u subtotu lor u
combnutons ol coumns, pus u row contunng the grund totu.
lrst, et's ook ut the exumpe tubes used n ths secton.
Thc fxampIc TabIcs
You' see the use ol the loowng tubes thut relne the representuton ol empoyees n our
mugnury store:
divisions, whch stores the dvsons wthn the compuny
jobs, whch stores the obs wthn the compuny
employees2, whch stores the empoyees
1hese tubes ure creuted by the store_schema.sql scrpt. 1he divisions tube s creuted
usng the loowng stutement:
CREATE TABLE divisions (
division_id CHAR(3)
CONSTRAINT divisions_pk PRIMARY KEY,
name VARCHAR2(15) NOT NULL
);
1he loowng query retreves the rows lrom the divisions tube:
SELECT *
FROM divisions;
DIV NAME
--- ----------
SAL Sales
Cruce Dutubuse ll SQL
OPE Operations
SUP Support
BUS Business
1he jobs tube s creuted usng the loowng stutement:
CREATE TABLE jobs (
job_id CHAR(3)
CONSTRAINT jobs_pk PRIMARY KEY,
name VARCHAR2(20) NOT NULL
);
1he next query retreves the rows lrom the jobs tube:
SELECT *
FROM jobs;
JOB NAME
--- ------------
WOR Worker
MGR Manager
ENG Engineer
TEC Technologist
PRE President
1he employees2 tube s creuted usng the loowng stutement:
CREATE TABLE employees2 (
employee_id INTEGER
CONSTRAINT employees2_pk PRIMARY KEY,
division_id CHAR(3)
CONSTRAINT employees2_fk_divisions
REFERENCES divisions(division_id),
job_id CHAR(3) REFERENCES jobs(job_id),
first_name VARCHAR2(10) NOT NULL,
last_name VARCHAR2(10) NOT NULL,
salary NUMBER(6, 0)
);
1he loowng query retreves the lrst lve rows lrom the employees2 tube:
SELECT *
FROM employees2
WHERE ROWNUM <= 5;
EMPLOYEE_ID DIV JOB FIRST_NAME LAST_NAME SALARY
----------- --- --- ---------- ---------- ----------
1 BUS PRE James Smith 800000
2 SAL MGR Ron Johnson 350000
3 SAL WOR Fred Hobbs 140000
4 SUP MGR Susan Jones 200000
5 SAL WOR Rob Green 350000
Chupter 7: Advunced Queres
20S
Using thc ROllUP CIausc
1he ROLLUP cuuse extends GROUP BY to return u row contunng u subtotu lor euch group ol
rows, pus u row contunng u totu lor u the groups.
As you suw n Chupter 4, you use GROUP BY to group rows nto bocks wth u common
coumn vuue. lor exumpe, the loowng query uses GROUP BY to group the rows lrom the
employees2 tube by department_id und uses SUM() to get the sum ol the suures lor euch
division_id:
SELECT division_id, SUM(salary)
FROM employees2
GROUP BY division_id
ORDER BY division_id;
DIV SUM(SALARY)
--- -----------
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
Passing a SingIc CuIumn tu ROllUP
1he loowng query rewrtes the prevous exumpe to use ROLLUP, notce the uddtonu row ut
the end, whch contuns the totu suures lor u the groups:
SELECT division_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(division_id)
ORDER BY division_id;
DIV SUM(SALARY)
--- -----------
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
8881000
NOTf
| ,ou nccd |hc rov n a pcc|c ordcr, ,ou houd uc an ORDER BY
cauc. You nccd |o do |h ]u| n cac Cracc Corpora|on dccdc |o
changc |hc dc|au| ordcr o| rov rc|urncd b, ROLLUP.
Passing MuItipIc CuIumns tu ROllUP
You cun puss mutpe coumns to ROLLUP, whch then groups the rows nto bocks wth the sume
coumn vuues. 1he loowng exumpe pusses the division_id und job_id coumns ol the
employees2 tube to ROLLUP, whch groups the rows by those coumns, n the output, notce
thut the suures ure summed by division_id und job_id, und thut ROLLUP returns u row
206
Cruce Dutubuse ll SQL
wth the sum ol the suures n euch division_id, pus u row ut the end wth the suury
grund totu:
SELECT division_id, job_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB SUM(SALARY)
--- --- -----------
BUS MGR 530000
BUS PRE 800000
BUS WOR 280000
BUS 1610000
OPE ENG 245000
OPE MGR 805000
OPE WOR 270000
OPE 1320000
SAL MGR 4446000
SAL WOR 490000
SAL 4936000
SUP MGR 465000
SUP TEC 115000
SUP WOR 435000
SUP 1015000
8881000
Changing thc Pusitiun uf CuIumns Passcd tu ROllUP
1he next exumpe swtches division_id und job_id, ths cuuses ROLLUP to cucuute the sum
ol the suures lor euch job_id:
SELECT job_id, division_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(job_id, division_id)
ORDER BY job_id, division_id;
JOB DIV SUM(SALARY)
--- --- -----------
ENG OPE 245000
ENG 245000
MGR BUS 530000
MGR OPE 805000
MGR SAL 4446000
MGR SUP 465000
MGR 6246000
PRE BUS 800000
PRE 800000
TEC SUP 115000
TEC 115000
WOR BUS 280000
WOR OPE 270000
Chupter 7: Advunced Queres
207
WOR SAL 490000
WOR SUP 435000
WOR 1475000
8881000
Using Othcr Aggrcgatc functiuns with ROllUP
You cun use uny ol the uggregute lunctons wth ROLLUP (lor u st ol the mun uggregute
lunctons, see 1ube 4-8 n Chupter 4). 1he loowng exumpe uses AVG() to cucuute the
uveruge suures:
SELECT division_id, job_id, AVG(salary)
FROM employees2
GROUP BY ROLLUP(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB AVG(SALARY)
--- --- -----------
BUS MGR 176666.667
BUS PRE 800000
BUS WOR 280000
BUS 322000
OPE ENG 245000
OPE MGR 201250
OPE WOR 135000
OPE 188571.429
SAL MGR 261529.412
SAL WOR 245000
SAL 259789.474
SUP MGR 232500
SUP TEC 115000
SUP WOR 145000
SUP 169166.667
240027.027
Using thc CUBf CIausc
1he CUBE cuuse extends GROUP BY to return rows contunng u subtotu lor u combnutons
ol coumns, pus u row contunng the grund totu. 1he loowng exumpe pusses division_id
und job_id to CUBE, whch groups the rows by those coumns:
SELECT division_id, job_id, SUM(salary)
FROM employees2
GROUP BY CUBE(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB SUM(SALARY)
--- --- -----------
BUS MGR 530000
BUS PRE 800000
BUS WOR 280000
BUS 1610000
Cruce Dutubuse ll SQL
OPE ENG 245000
OPE MGR 805000
OPE WOR 270000
OPE 1320000
SAL MGR 4446000
SAL WOR 490000
SAL 4936000
SUP MGR 465000
SUP TEC 115000
SUP WOR 435000
SUP 1015000
ENG 245000
MGR 6246000
PRE 800000
TEC 115000
WOR 1475000
8881000
Notce thut the suures ure summed by division_id und job_id. CUBE returns u row wth
the sum ol the suures lor euch division_id, uong wth the sum ol u suures lor euch job_
id neur the end. At the very end s u row wth the grund totu ol the suures.
1he next exumpe swtches division_id und job_id:
SELECT job_id, division_id, SUM(salary)
FROM employees2
GROUP BY CUBE(job_id, division_id)
ORDER BY job_id, division_id;
JOB DIV SUM(SALARY)
--- --- -----------
ENG OPE 245000
ENG 245000
MGR BUS 530000
MGR OPE 805000
MGR SAL 4446000
MGR SUP 465000
MGR 6246000
PRE BUS 800000
PRE 800000
TEC SUP 115000
TEC 115000
WOR BUS 280000
WOR OPE 270000
WOR SAL 490000
WOR SUP 435000
WOR 1475000
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
8881000
Chupter 7: Advunced Queres
209
Using thc GROUPlNG() functiun
1he GROUPING() luncton uccepts u coumn und returns 0 or l. GROUPING() returns l when
the coumn vuue s nu und returns 0 when the coumn vuue s non-nu. GROUPING() s used
ony n queres thut use ROLLUP or CUBE. GROUPING() s uselu when you wunt to dspuy u
vuue when u nu woud otherwse be returned.
Using GROUPlNG() with a SingIc CuIumn in a ROllUP
As you suw eurer n the secton lussng u Snge Coumn to RCLLLl, the ust row n the
exumpe's resut set contuned u totu ol the suures:
SELECT division_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(division_id)
ORDER BY division_id;
DIV SUM(SALARY)
--- -----------
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
8881000
1he division_id coumn lor the ust row s nu. You cun use the GROUPING() luncton
to determne whether ths coumn s nu, us shown n the loowng query, notce GROUPING()
returns 0 lor the rows thut huve non-nu division_id vuues und returns l lor the ust row thut
hus u nu division_id:
SELECT GROUPING(division_id), division_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(division_id)
ORDER BY division_id;
GROUPING(DIVISION_ID) DIV SUM(SALARY)
--------------------- --- -----------
0 BUS 1610000
0 OPE 1320000
0 SAL 4936000
0 SUP 1015000
1 8881000
Using CASf tu Cunvcrt thc Rcturncd VaIuc frum GROUPlNG()
You cun use the CASE expresson to convert the l n the prevous exumpe to u meunnglu vuue.
1he loowng exumpe uses CASE to convert l to the strng 'All divisions':
SELECT
CASE GROUPING(division_id)
WHEN 1 THEN 'All divisions'
ELSE division_id
END AS div,
SUM(salary)
210
Cruce Dutubuse ll SQL
FROM employees2
GROUP BY ROLLUP(division_id)
ORDER BY division_id;
DIV SUM(SALARY)
------------- -----------
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
All divisions 8881000
Using CASf and GROUPlNG() tu Cunvcrt MuItipIc CuIumn VaIucs
1he next exumpe extends the deu ol repucng nu vuues to u ROLLUP contunng mutpe
coumns (division_id und job_id), notce thut nu division_id vuues ure repuced wth
the strng 'All divisions' und thut nu job_id vuues ure repuced wth 'All jobs':
SELECT
CASE GROUPING(division_id)
WHEN 1 THEN 'All divisions'
ELSE division_id
END AS div,
CASE GROUPING(job_id)
WHEN 1 THEN 'All jobs'
ELSE job_id
END AS job,
SUM(salary)
FROM employees2
GROUP BY ROLLUP(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB SUM(SALARY)
------------- -------- -----------
BUS MGR 530000
BUS PRE 800000
BUS WOR 280000
BUS All jobs 1610000
OPE ENG 245000
OPE MGR 805000
OPE WOR 270000
OPE All jobs 1320000
SAL MGR 4446000
SAL WOR 490000
SAL All jobs 4936000
SUP MGR 465000
SUP TEC 115000
SUP WOR 435000
SUP All jobs 1015000
All divisions All jobs 8881000
Using GROUPlNG() with CUBf
You cun use the GROUPING() luncton wth CUBE, us n ths exumpe:
Chupter 7: Advunced Queres
211
SELECT
CASE GROUPING(division_id)
WHEN 1 THEN 'All divisions'
ELSE division_id
END AS div,
CASE GROUPING(job_id)
WHEN 1 THEN 'All jobs'
ELSE job_id
END AS job,
SUM(salary)
FROM employees2
GROUP BY CUBE(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB SUM(SALARY)
------------- -------- -----------
BUS MGR 530000
BUS PRE 800000
BUS WOR 280000
BUS All jobs 1610000
OPE ENG 245000
OPE MGR 805000
OPE WOR 270000
OPE All jobs 1320000
SAL MGR 4446000
SAL WOR 490000
SAL All jobs 4936000
SUP MGR 465000
SUP TEC 115000
SUP WOR 435000
SUP All jobs 1015000
All divisions ENG 245000
All divisions MGR 6246000
All divisions PRE 800000
All divisions TEC 115000
All divisions WOR 1475000
All divisions All jobs 8881000
Using thc GROUPlNG SfTS CIausc
You use the GROUPING SETS cuuse to get ust the subtotu rows. 1he loowng exumpe uses
GROUPING SETS to get the subtotus lor suures by division_id und job_id:
SELECT division_id, job_id, SUM(salary)
FROM employees2
GROUP BY GROUPING SETS(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB SUM(SALARY)
--- --- -----------
BUS 1610000
OPE 1320000
212
Cruce Dutubuse llg SQL
SAL 4936000
SUP 1015000
ENG 245000
MGR 6246000
PRE 800000
TEC 115000
WOR 1475000
Notce thut ony subtotus lor the division_id und job_id coumns ure returned, the totu
lor u suures s not returned. You' see how to get the totu us we us the subtotus usng the
GROUPING_ID() luncton n the next secton.
TlP
Thc GROUPING SETS cauc |,pca, o||cr bc||cr pcr|ormancc |han
CUBE. Thcrc|orc, ,ou houd uc GROUPING SETS ra|hcr |han CUBE
vhcrcvcr pobc.
Using thc GROUPlNG_lD() functiun
You cun use the GROUPING_ID() luncton to lter rows usng u HAVING cuuse to excude rows
thut don't contun u subtotu or totu. 1he GROUPING_ID() luncton uccepts one or more coumns
und returns the decmu equvuent ol the GROUPING bt vector. 1he GROUPING bt vector s
computed by combnng the resuts ol u cu to the GROUPING() luncton lor euch coumn n order.
Cumputing thc GROUPlNG Bit Vcctur
Lurer n the secton Lsng the CRCLllNC() luncton, you suw thut GROUPING() returns l
when the coumn vuue s nu und returns 0 when the coumn vuue s non-nu, lor exumpe:
ll both division_id und job_id ure non-nu, GROUPING() returns 0 lor both
coumns. 1he resut lor division_id s combned wth the resut lor job_id, gvng
u bt vector ol 00, whose decmu equvuent s 0. GROUPING_ID() therelore returns
0 when division_id und job_id ure non-nu.
ll division_id s non-nu (the GROUPING bt s 0), but job_id s nu (the GROUPING
bt s l), the resutng bt vector s 0l und GROUPING_ID() returns l.
ll division_id s nu (the GROUPING bt s l), but job_id s non-nu (the GROUPING
bt s 0), the resutng bt vector s l0 und GROUPING_ID() returns 2.
ll both division_id und job_id ure nu (both GROUPING bts ure 0), the bt vector s
ll und GROUPING_ID() returns 3.
1he loowng tube summurzes these resuts.
divisiun_id jub_id Bit Vcctur GROUPlNG_lD() Rcturn VaIuc
non-nu non-nu 00 0
non-nu nu 0l l
nu non-nu l0 2
nu nu ll 3
Chupter 7: Advunced Queres
213
An fxampIc Qucry That lIIustratcs thc Usc uf GROUPlNG_lD()
1he loowng exumpe pusses division_id und job_id to GROUPING_ID(), notce thut the
output lrom the GROUPING_ID() luncton ugrees wth the expected returned vuues documented
n the prevous secton:
SELECT
division_id, job_id,
GROUPING(division_id) AS DIV_GRP,
GROUPING(job_id) AS JOB_GRP,
GROUPING_ID(division_id, job_id) AS grp_id,
SUM(salary)
FROM employees2
GROUP BY CUBE(division_id, job_id)
ORDER BY division_id, job_id;
DIV JOB DIV_GRP JOB_GRP GRP_ID SUM(SALARY)
--- --- ---------- ---------- ---------- -----------
BUS MGR 0 0 0 530000
BUS PRE 0 0 0 800000
BUS WOR 0 0 0 280000
BUS 0 1 1 1610000
OPE ENG 0 0 0 245000
OPE MGR 0 0 0 805000
OPE WOR 0 0 0 270000
OPE 0 1 1 1320000
SAL MGR 0 0 0 4446000
SAL WOR 0 0 0 490000
SAL 0 1 1 4936000
SUP MGR 0 0 0 465000
SUP TEC 0 0 0 115000
SUP WOR 0 0 0 435000
SUP 0 1 1 1015000
ENG 1 0 2 245000
MGR 1 0 2 6246000
PRE 1 0 2 800000
TEC 1 0 2 115000
WOR 1 0 2 1475000
1 1 3 8881000
A UscfuI AppIicatiun uf GROUPlNG_lD()
Cne uselu uppcuton ol GROUPING_ID() s to lter rows usng u HAVING cuuse. 1he HAVING
cuuse cun excude rows thut don't contun u subtotu or totu by smpy checkng l GROUPING_
ID() returns u vuue greuter thun 0. lor exumpe:
SELECT
division_id, job_id,
GROUPING_ID(division_id, job_id) AS grp_id,
SUM(salary)
FROM employees2
GROUP BY CUBE(division_id, job_id)
214
Cruce Dutubuse ll SQL
HAVING GROUPING_ID(division_id, job_id) > 0
ORDER BY division_id, job_id;
DIV JOB GRP_ID SUM(SALARY)
--- --- ---------- -----------
BUS 1 1610000
OPE 1 1320000
SAL 1 4936000
SUP 1 1015000
ENG 2 245000
MGR 2 6246000
PRE 2 800000
TEC 2 115000
WOR 2 1475000
3 8881000
Using a CuIumn MuItipIc Timcs in a GROUP BY CIausc
You cun use u coumn muny tmes n u GROUP BY cuuse. Dong ths uows you to reorgunze
your dutu or report on dllerent groupngs ol dutu. lor exumpe, the loowng query contuns u
GROUP BY cuuse thut uses division_id twce, once to group by division_id und ugun
n u ROLLUP:
SELECT division_id, job_id, SUM(salary)
FROM employees2
GROUP BY division_id, ROLLUP(division_id, job_id);
DIV JOB SUM(SALARY)
--- --- -----------
BUS MGR 530000
BUS PRE 800000
BUS WOR 280000
OPE ENG 245000
OPE MGR 805000
OPE WOR 270000
SAL MGR 4446000
SAL WOR 490000
SUP MGR 465000
SUP TEC 115000
SUP WOR 435000
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
BUS 1610000
OPE 1320000
SAL 4936000
SUP 1015000
Notce, however, thut the ust lour rows ure dupcutes ol the prevous lour rows. You cun
emnute these dupcutes usng the GROUP_ID() luncton, whch you' eurn ubout next.
Chupter 7: Advunced Queres
21S
Using thc GROUP_lD() functiun
You cun use the GROUP_ID() luncton to remove dupcute rows returned by u GROUP BY
cuuse. GROUP_ID() doesn't uccept uny purumeters. ll dupcutes exst lor u purtcuur
groupng, GROUP_ID returns numbers n the runge 0 to l.
1he loowng exumpe rewrtes the query shown n the prevous secton to ncude the output
lrom GROUP_ID(), notce thut GROUP_ID() returns 0 lor u rows except the ust lour, whch ure
dupcutes ol the prevous lour rows, und thut GROUP_ID() returns l:
SELECT division_id, job_id, GROUP_ID(), SUM(salary)
FROM employees2
GROUP BY division_id, ROLLUP(division_id, job_id);
DIV JOB GROUP_ID() SUM(SALARY)
--- --- ---------- -----------
BUS MGR 0 530000
BUS PRE 0 800000
BUS WOR 0 280000
OPE ENG 0 245000
OPE MGR 0 805000
OPE WOR 0 270000
SAL MGR 0 4446000
SAL WOR 0 490000
SUP MGR 0 465000
SUP TEC 0 115000
SUP WOR 0 435000
BUS 0 1610000
OPE 0 1320000
SAL 0 4936000
SUP 0 1015000
BUS 1 1610000
OPE 1 1320000
SAL 1 4936000
SUP 1 1015000
You cun emnute dupcute rows usng u HAVING cuuse thut uows ony rows whose
GROUP_ID() s 0, lor exumpe:
SELECT division_id, job_id, GROUP_ID(), SUM(salary)
FROM employees2
GROUP BY division_id, ROLLUP(division_id, job_id)
HAVING GROUP_ID() = 0;
DIV JOB GROUP_ID() SUM(SALARY)
--- --- ---------- -----------
BUS MGR 0 530000
BUS PRE 0 800000
BUS WOR 0 280000
OPE ENG 0 245000
OPE MGR 0 805000
OPE WOR 0 270000
216
Cruce Dutubuse ll SQL
SAL MGR 0 4446000
SAL WOR 0 490000
SUP MGR 0 465000
SUP TEC 0 115000
SUP WOR 0 435000
BUS 0 1610000
OPE 0 1320000
SAL 0 4936000
SUP 0 1015000
1hs concudes the dscusson ol the extended GROUP BY cuuses.
Using thc AnaIytic functiuns
1he dutubuse hus muny but-n unuytc lunctons thut enube you to perlorm compex
cucuutons, such us lndng the top-seng product type lor euch month, the top suespersons,
und so on. 1he unuytc lunctons ure orgunzed nto the loowng cutegores:
Ranking functiuns enube you to cucuute runks, percentes, und -tes (tertes,
quurtes, und so on).
lnvcrsc pcrccntiIc functiuns enube you to cucuute the vuue thut corresponds to u
percente.
Winduw functiuns enube you to cucuute cumuutve und movng uggregutes.
Rcpurting functiuns enube you to cucuute thngs ke murket shure.
lag and Icad functiuns enube you to get u vuue n u row where thut row s u certun
number ol rows uwuy lrom the current row.
first and Iast functiuns enube you to get the lrst und ust vuues n un ordered group.
lincar rcgrcssiun functiuns enube you to lt un ordnury-eust-squures regresson ne
to u set ol number purs.
HyputhcticaI rank and distributiun functiuns enube you to cucuute the runk und
percente thut u new row woud huve l you nserted t nto u tube.
You' eurn ubout these lunctons shorty, but lrst et's exumne the exumpe tube used next.
Thc fxampIc TabIc
You' see the use ol the all_sales tube n the loowng sectons. 1he all_sales tube stores
the sum ol u the sues by dour umount lor u purtcuur yeur, month, product type, und empoyee.
1he all_sales tube s creuted by the store_schema.sql scrpt us loows:
CREATE TABLE all_sales (
year INTEGER NOT NULL,
month INTEGER NOT NULL,
prd_type_id INTEGER
CONSTRAINT all_sales_fk_product_types
REFERENCES product_types(product_type_id),
emp_id INTEGER
Chupter 7: Advunced Queres
217
CONSTRAINT all_sales_fk_employees2
REFERENCES employees2(employee_id),
amount NUMBER(8, 2),
CONSTRAINT all_sales_pk PRIMARY KEY (
year, month, prd_type_id, emp_id
)
);
As you cun see, the all_sales tube contuns lve coumns, whch ure us loows:
YEAR stores the yeur the sues took puce.
MONTH stores the month the sues took puce (l to l2).
PRD_TYPE_ID stores the product_type_id ol the product.
EMP_ID stores the employee_id ol the empoyee who hunded the sues.
AMOUNT stores the totu dour umount ol the sues.
1he loowng query retreves the lrst l2 rows lrom the all_sales tube:
SELECT *
FROM all_sales
WHERE ROWNUM <= 12;
YEAR MONTH PRD_TYPE_ID EMP_ID AMOUNT
---------- ---------- ----------- ---------- ----------
2003 1 1 21 10034.84
2003 2 1 21 15144.65
2003 3 1 21 20137.83
2003 4 1 21 25057.45
2003 5 1 21 17214.56
2003 6 1 21 15564.64
2003 7 1 21 12654.84
2003 8 1 21 17434.82
2003 9 1 21 19854.57
2003 10 1 21 21754.19
2003 11 1 21 13029.73
2003 12 1 21 10034.84
NOTf
Thc all_sales |abc ac|ua, con|an a o| morc rov |han |h, bu|
|or pacc condcra|on 'vc om||cd |ng |hcm a hcrc.
Let's exumne the runkng lunctons next.
Using thc Ranking functiuns
You use the runkng lunctons to cucuute runks, percentes, und n-tes. 1he runkng lunctons
ure shown n 1ube 7-2.
Let's exumne the RANK() und DENSE_RANK() lunctons lrst.
218
Cruce Dutubuse llg SQL
Using thc RANk() and DfNSf_RANk() functiuns
You use RANK() und DENSE_RANK() to runk tems n u group. 1he dllerence between these two
lunctons s n the wuy they hunde tems thut te: RANK() euves u gup n the sequence when
there s u te, but DENSE_RANK() euves no gups. lor exumpe, l you were runkng sues by
product type und two product types te lor lrst puce, RANK() woud put the two product types n
lrst puce, but the next product type woud be n thrd puce. DENSE_RANK() woud uso put the
two product types n lrst puce, but the next product type woud be n second puce.
1he loowng query ustrutes the use ol RANK() und DENSE_RANK() to get the runkng ol
sues by product type lor the yeur 2003, notce the use ol the keyword OVER n the syntux when
cung the RANK() und DENSE_RANK() lunctons:
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank,
DENSE_RANK() OVER (ORDER BY SUM(amount) DESC) AS dense_rank
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) RANK DENSE_RANK
----------- ----------- ---------- ----------
1 905081.84 1 1
2 186381.22 4 4
3 478270.91 2 2
4 402751.16 3 3
Notce thut sues lor product type rl ure runked lrst, sues lor product type r2 ure runked
lourth, und so on. ecuuse there ure no tes, RANK() und DENSE_RANK() return the sume runks.
functiun Dcscriptiun
RANK()
Returns the runk ol tems n u group. RANK() euves u gup n the
sequence ol runkngs n the event ol u te.
DENSE_RANK()
Returns the runk ol tems n u group. DENSE_RANK() doesn't euve
u gup n the sequence ol runkngs n the event ol u te.
CUME_DIST()
Returns the poston ol u specled vuue reutve to u group ol
vuues. CUME_DIST() s short lor cumuutve dstrbuton.
PERCENT_RANK()
Returns the percent runk ol u vuue reutve to u group ol vuues.
NTILE()
Returns n-tes: tertes, quurtes, und so on.
ROW_NUMBER()
Returns u number wth euch row n u group.
TABlf 7-2 Thc Ran|ng unc|on
Chupter 7: Advunced Queres
1he all_sales tube uctuuy contuns nus n the AMOUNT coumn lor u rows whose
PRD_TYPE_ID coumn s 5, the prevous query omts these rows becuuse ol the ncuson ol the
ne "AND amount IS NOT NULL" n the WHERE cuuse. 1he next exumpe ncudes these rows
by euvng out the AND ne lrom the WHERE cuuse:
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank,
DENSE_RANK() OVER (ORDER BY SUM(amount) DESC) AS dense_rank
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) RANK DENSE_RANK
----------- ----------- ---------- ----------
1 905081.84 2 2
2 186381.22 5 5
3 478270.91 3 3
4 402751.16 4 4
5 1 1
Notce thut the ust row contuns nu lor the sum ol the AMOUNT coumn und thut RANK() und
DENSE_RANK() return l lor ths row. 1hs s becuuse by deluut RANK() und DENSE_RANK()
ussgn the hghest runk ol l to nu vuues n descendng runkngs (thut s, DESC s used n the
OVER cuuse) und the owest runk n uscendng runkngs (thut s, ASC s used n the OVER cuuse).
Contro||ing kanking of hu|| a|ues using the huLL5 fIk51 and huLL5 Lk51 C|auses \hen
usng un unuytc luncton, you cun expcty contro whether nus ure the hghest or owest n
u group usng NULLS FIRST or NULLS LAST. 1he loowng exumpe uses NULLS LAST to
specly thut nus ure the owest:
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC NULLS LAST) AS rank,
DENSE_RANK() OVER (ORDER BY SUM(amount) DESC NULLS LAST) AS dense_rank
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) RANK DENSE_RANK
----------- ----------- ---------- ----------
1 905081.84 1 1
2 186381.22 4 4
3 478270.91 2 2
4 402751.16 3 3
5 5 5
Cruce Dutubuse ll SQL
using the Fkk1I1I0h 8 C|ause with kna|ytic functions You use the PARTITION BY cuuse
wth the unuytc lunctons when you need to dvde the groups nto subgroups. lor exumpe, l
you need to subdvde the sues umount by month, you cun use PARTITION BY month, us
shown n the loowng query:
SELECT
prd_type_id, month, SUM(amount),
RANK() OVER (PARTITION BY month ORDER BY SUM(amount) DESC) AS rank
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id, month
ORDER BY prd_type_id, month;
PRD_TYPE_ID MONTH SUM(AMOUNT) RANK
----------- ---------- ----------- ----------
1 1 38909.04 1
1 2 70567.9 1
1 3 91826.98 1
1 4 120344.7 1
1 5 97287.36 1
1 6 57387.84 1
1 7 60929.04 2
1 8 75608.92 1
1 9 85027.42 1
1 10 105305.22 1
1 11 55678.38 1
1 12 46209.04 2
2 1 14309.04 4
2 2 13367.9 4
2 3 16826.98 4
2 4 15664.7 4
2 5 18287.36 4
2 6 14587.84 4
2 7 15689.04 3
2 8 16308.92 4
2 9 19127.42 4
2 10 13525.14 4
2 11 16177.84 4
2 12 12509.04 4
3 1 24909.04 2
3 2 15467.9 3
3 3 20626.98 3
3 4 23844.7 2
3 5 18687.36 3
3 6 19887.84 3
3 7 81589.04 1
3 8 62408.92 2
3 9 46127.42 3
3 10 70325.29 3
3 11 46187.38 2
3 12 48209.04 1
Chupter 7: Advunced Queres
4 1 17398.43 3
4 2 17267.9 2
4 3 31026.98 2
4 4 16144.7 3
4 5 20087.36 2
4 6 33087.84 2
4 7 12089.04 4
4 8 58408.92 3
4 9 49327.42 2
4 10 75325.14 2
4 11 42178.38 3
4 12 30409.05 3
using k0LLuF, Cu8, and 6k0uFIh6 515 0perators with kna|ytic functions You cun use the
ROLLUP, CUBE, und GROUPING SETS operutors wth the unuytc lunctons. 1he loowng query
uses ROLLUP und RANK() to get the sues runkngs by product type lD:
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank
FROM all_sales
WHERE year = 2003
GROUP BY ROLLUP(prd_type_id)
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) RANK
----------- ----------- ----------
1 905081.84 3
2 186381.22 6
3 478270.91 4
4 402751.16 5
5 1
1972485.13 2
1he next query uses CUBE und RANK() to get u runkngs ol sues by product type lD und
empoyee lD:
SELECT
prd_type_id, emp_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank
FROM all_sales
WHERE year = 2003
GROUP BY CUBE(prd_type_id, emp_id)
ORDER BY prd_type_id, emp_id;
PRD_TYPE_ID EMP_ID SUM(AMOUNT) RANK
----------- ---------- ----------- ----------
1 21 197916.96 19
1 22 214216.96 17
1 23 98896.96 26
1 24 207216.96 18
1 25 93416.96 28
1 26 93417.04 27
Cruce Dutubuse ll SQL
1 905081.84 9
2 21 20426.96 40
2 22 19826.96 41
2 23 19726.96 42
2 24 43866.96 34
2 25 32266.96 38
2 26 50266.42 31
2 186381.22 21
3 21 140326.96 22
3 22 116826.96 23
3 23 112026.96 24
3 24 34829.96 36
3 25 29129.96 39
3 26 45130.11 33
3 478270.91 10
4 21 108326.96 25
4 22 81426.96 30
4 23 92426.96 29
4 24 47456.96 32
4 25 33156.96 37
4 26 39956.36 35
4 402751.16 13
5 21 1
5 22 1
5 23 1
5 24 1
5 25 1
5 26 1
5 1
21 466997.84 11
22 432297.84 12
23 323077.84 15
24 333370.84 14
25 187970.84 20
26 228769.93 16
1972485.13 8
1he next query uses GROUPING SETS und RANK() to get ust the sues umount subtotu
runkngs:
SELECT
prd_type_id, emp_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank
FROM all_sales
WHERE year = 2003
GROUP BY GROUPING SETS(prd_type_id, emp_id)
ORDER BY prd_type_id, emp_id;
PRD_TYPE_ID EMP_ID SUM(AMOUNT) RANK
----------- ---------- ----------- ----------
1 905081.84 2
2 186381.22 11
Chupter 7: Advunced Queres
223
3 478270.91 3
4 402751.16 6
5 1
21 466997.84 4
22 432297.84 5
23 323077.84 8
24 333370.84 7
25 187970.84 10
26 228769.93 9
Using thc CUMf_DlST() and PfRCfNT_RANk() functiuns
You use CUME_DIST() to cucuute the poston ol u specled vuue reutve to u group ol vuues,
CUME_DIST() s short lor cumuutve dstrbuton. You use PERCENT_RANK() to cucuute the
percent runk ol u vuue reutve to u group ol vuues.
1he loowng query ustrutes the use ol CUME_DIST() und PERCENT_RANK() to get the
cumuutve dstrbuton und percent runk ol sues:
SELECT
prd_type_id, SUM(amount),
CUME_DIST() OVER (ORDER BY SUM(amount) DESC) AS cume_dist,
PERCENT_RANK() OVER (ORDER BY SUM(amount) DESC) AS percent_rank
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) CUME_DIST PERCENT_RANK
----------- ----------- ---------- ------------
1 905081.84 .4 .25
2 186381.22 1 1
3 478270.91 .6 .5
4 402751.16 .8 .75
5 .2 0
Using thc NTllf() functiun
You use NTILE(buckets) to cucuute -tes (tertes, quurtes, und so on), buckets specles
the number ol buckets nto whch groups ol rows ure puced. lor exumpe, NTILE(2) specles
two buckets und therelore dvdes the rows nto two groups ol rows, NTILE(4) dvdes the groups
nto lour buckets und therelore dvdes the rows nto lour groups.
1he loowng query ustrutes the use ol NTILE(), notce thut 4 s pussed to NTILE() to
spt the groups ol rows nto lour buckets:
SELECT
prd_type_id, SUM(amount),
NTILE(4) OVER (ORDER BY SUM(amount) DESC) AS ntile
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id
ORDER BY prd_type_id;
224
Cruce Dutubuse ll SQL
PRD_TYPE_ID SUM(AMOUNT) NTILE
----------- ----------- ----------
1 905081.84 1
2 186381.22 4
3 478270.91 2
4 402751.16 3
Using thc ROW_NUMBfR() functiun
You use ROW_NUMBER() to return u number wth euch row n u group, sturtng ut l. 1he loowng
query ustrutes the use ol ROW_NUMBER():
SELECT
prd_type_id, SUM(amount),
ROW_NUMBER() OVER (ORDER BY SUM(amount) DESC) AS row_number
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) ROW_NUMBER
----------- ----------- ----------
1 905081.84 2
2 186381.22 5
3 478270.91 3
4 402751.16 4
5 1
1hs concudes the dscusson ol runkng lunctons.
Using thc lnvcrsc PcrccntiIc functiuns
ln the secton Lsng the CLML_DlS1() und lLRCLN1_RANK() lunctons, you suw thut CUME_
DIST() s used to cucuute the poston ol u specled vuue reutve to u group ol vuues. You
uso suw thut PERCENT_RANK() s used to cucuute the percent runk ol u vuue reutve to u
group ol vuues.
ln ths secton, you' see how to use the nverse percente lunctons to get the vuue thut
corresponds to u percente. 1here ure two nverse percente lunctons: PERCENTILE_DISC(x)
und PERCENTILE_CONT(x). 1hey operute n u munner the reverse ol CUME_DIST() und
PERCENT_RANK(). PERCENTILE_DISC(x) exumnes the cumuutve dstrbuton vuues n
euch group unt t lnds one thut s greuter thun or equu to x. PERCENTILE_CONT(x) exumnes
the percent runk vuues n euch group unt t lnds one thut s greuter thun or equu to x.
1he loowng query ustrutes the use ol PERCENTILE_CONT() und PERCENTILE_DISC()
to get the sum ol the umount whose percente s greuter thun or equu to 0.6:
SELECT
PERCENTILE_CONT(0.6) WITHIN GROUP (ORDER BY SUM(amount) DESC)
AS percentile_cont,
PERCENTILE_DISC(0.6) WITHIN GROUP (ORDER BY SUM(amount) DESC)
AS percentile_disc
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id;
Chupter 7: Advunced Queres
22S
PERCENTILE_CONT PERCENTILE_DISC
--------------- ---------------
417855.11 402751.16
ll you compure the sum ol the umounts shown n these resuts wth those shown n the eurer
secton Lsng the CLML_DlS1() und lLRCLN1_RANK() lunctons, you' see thut the sums
correspond to those whose cumuutve dstrbuton und percent runk ure 0.6 und 0.75, respectvey.
Using thc Winduw functiuns
You use the wndow lunctons to cucuute thngs ke cumuutve sums und movng uveruges
wthn u specled runge ol rows, u runge ol vuues, or un ntervu ol tme. As you know, u query
returns u set ol rows known us the resut set. 1he term wndow s used to descrbe u subset ol
rows wthn the resut set. 1he subset ol rows seen through the wndow s then processed by the
wndow lunctons, whch return u vuue. You cun delne the sturt und end ol the wndow.
You cun use u wndow wth the loowng lunctons: SUM(), AVG(), MAX(), MIN(), COUNT(),
VARIANCE(), und STDDEV(), you suw these lunctons n Chupter 4. You cun uso use u wndow
wth FIRST_VALUE() und LAST_VALUE(), whch return the lrst und ust vuues n u wndow.
(You' eurn more ubout the FIRST_VALUE() und LAST_VALUE() lunctons uter n the secton
Cettng the lrst und Lust Rows Lsng llRS1_VALLL() und LAS1_VALLL().)
ln the next secton, you' see how to perlorm u cumuutve sum, u movng uveruge, und u
centered uveruge.
Pcrfurming a CumuIativc Sum
1he loowng query perlorms u cumuutve sum to compute the cumuutve sues umount lor
2003, sturtng wth }unuury und endng n December, notce thut euch monthy sues umount s
udded to the cumuutve umount thut grows ulter euch month:
SELECT
month, SUM(amount) AS month_amount,
SUM(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS cumulative_amount
FROM all_sales
WHERE year = 2003
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT CUMULATIVE_AMOUNT
---------- ------------ -----------------
1 95525.55 95525.55
2 116671.6 212197.15
3 160307.92 372505.07
4 175998.8 548503.87
5 154349.44 702853.31
6 124951.36 827804.67
7 170296.16 998100.83
8 212735.68 1210836.51
9 199609.68 1410446.19
10 264480.79 1674926.98
11 160221.98 1835148.96
12 137336.17 1972485.13
Cruce Dutubuse ll SQL
1hs query uses the loowng expresson to compute the cumuutve uggregute:
SUM(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS cumulative_amount
Let's breuk down ths expresson:
SUM(amount) computes the sum ol un umount. 1he outer SUM() computes the
cumuutve umount.
ORDER BY month orders the rows reud by the query by month.
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW delnes the sturt und
end ol the wndow. 1he sturt s set to UNBOUNDED PRECEDING, whch meuns the sturt ol
the wndow s lxed ut the lrst row n the resut set returned by the query. 1he end ol the
wndow s set to CURRENT ROW, CURRENT ROW represents the current row n the resut
set beng processed, und the end ol the wndow sdes down one row ulter the outer
SUM() luncton computes und returns the current cumuutve umount.
1he entre query computes und returns the cumuutve totu ol the sues umounts, sturtng ut
month l, und then uddng the sues umount lor month 2, then month 3, und so on, up to und
ncudng month l2. 1he sturt ol the wndow s lxed ut month l, but the bottom ol the wndow
moves down one row n the resut set ulter euch month's sues umounts ure udded to the cumuutve
totu. 1hs contnues unt the ust row n the resut set s processed by the wndow und the SUM()
lunctons.
Don't conluse the end ol the wndow wth the end ol the resut set. ln the prevous exumpe,
the end ol the wndow sdes down one row n the resut set us euch row s processed (.e., the
sum ol the sues umount lor thut month s udded to the cumuutve totu). ln the exumpe, the end
ol the wndow sturts ut the lrst row, the sum sues umount lor thut month s udded to the cumuutve
totu, und then the end ol the wndow moves down one row to the second row. At ths pont, the
wndow sees two rows. 1he sum ol the sues umount lor thut month s udded to the cumuutve
totu, und the end ol the wndow moves down one row to the thrd row. At ths pont, the wndow
sees three rows. 1hs contnues unt the twelth row s processed. At ths pont, the wndow sees
tweve rows.
1he loowng query uses u cumuutve sum to compute the cumuutve sues umount, sturtng
wth }une ol 2003 (month 6) und endng n December ol 2003 (month l2):
SELECT
month, SUM(amount) AS month_amount,
SUM(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS
cumulative_amount
FROM all_sales
WHERE year = 2003
AND month BETWEEN 6 AND 12
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT CUMULATIVE_AMOUNT
---------- ------------ -----------------
6 124951.36 124951.36
7 170296.16 295247.52
Chupter 7: Advunced Queres
227
8 212735.68 507983.2
9 199609.68 707592.88
10 264480.79 972073.67
11 160221.98 1132295.65
12 137336.17 1269631.82
Pcrfurming a Muving Avcragc
1he loowng query computes the movng uveruge ol the sues umount between the current
month und the prevous three months:
SELECT
month, SUM(amount) AS month_amount,
AVG(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
AS moving_average
FROM all_sales
WHERE year = 2003
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT MOVING_AVERAGE
---------- ------------ --------------
1 95525.55 95525.55
2 116671.6 106098.575
3 160307.92 124168.357
4 175998.8 137125.968
5 154349.44 151831.94
6 124951.36 153901.88
7 170296.16 156398.94
8 212735.68 165583.16
9 199609.68 176898.22
10 264480.79 211780.578
11 160221.98 209262.033
12 137336.17 190412.155
Notce thut the query uses the loowng expresson to compute the movng uveruge:
AVG(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
AS moving_average
Let's breuk down ths expresson:
SUM(amount) computes the sum ol un umount. 1he outer AVG() computes the uveruge.
ORDER BY month orders the rows reud by the query by month.
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW delnes the sturt ol the wndow
us ncudng the three rows precedng the current row, the end ol the wndow s the
current row beng processed.
So, the entre expresson computes the movng uveruge ol the sues umount between the
current month und the prevous three months. ecuuse lor the lrst two months ess thun the lu
three months ol dutu ure uvuube, the movng uveruge s bused on ony the months uvuube.
228
Cruce Dutubuse ll SQL
oth the sturt und the end ol the wndow begn ut row rl reud by the query. 1he end ol the
wndow moves down ulter euch row s processed. 1he sturt ol the wndow moves down ony ulter
row r4 hus been processed, und subsequenty moves down one row ulter euch row s processed.
1hs contnues unt the ust row n the resut set s reud.
Pcrfurming a Ccntcrcd Avcragc
1he loowng query computes the movng uveruge ol the sues umount centered between the
prevous und next month lrom the current month:
SELECT
month, SUM(amount) AS month_amount,
AVG(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS moving_average
FROM all_sales
WHERE year = 2003
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT MOVING_AVERAGE
---------- ------------ --------------
1 95525.55 106098.575
2 116671.6 124168.357
3 160307.92 150992.773
4 175998.8 163552.053
5 154349.44 151766.533
6 124951.36 149865.653
7 170296.16 169327.733
8 212735.68 194213.84
9 199609.68 225608.717
10 264480.79 208104.15
11 160221.98 187346.313
12 137336.17 148779.075
Notce thut the query uses the loowng expresson to compute the movng uveruge:
AVG(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS moving_average
Let's breuk down ths expresson:
SUM(amount) computes the sum ol un umount. 1he outer AVG() computes the uveruge.
ORDER BY month orders the rows reud by the query by month.
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING delnes the sturt ol the wndow us
ncudng the row precedng the current row beng processed. 1he end ol the wndow s
the row loowng the current row.
So, the entre expresson computes the movng uveruge ol the sues umount between the current
month und the prevous month. ecuuse lor the lrst und ust month ess thun the lu three months
ol dutu ure uvuube, the movng uveruge s bused on ony the months uvuube.
Chupter 7: Advunced Queres
229
1he sturt ol the wndow begns ut row rl reud by the query. 1he end ol the wndow begns
ut row r2 und moves down ulter euch row s processed. 1he sturt ol the wndow moves down
ony once row r2 hus been processed. lrocessng contnues unt the ust row reud by the query
s processed.
Gctting thc first and last Ruws Using flRST_VAlUf() and lAST_VAlUf()
You use the FIRST_VALUE() und LAST_VALUE() lunctons to get the lrst und ust rows n u
wndow. 1he loowng query uses FIRST_VALUE() und LAST_VALUE() to get the prevous
und next month's sues umount:
SELECT
month, SUM(amount) AS month_amount,
FIRST_VALUE(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS previous_month_amount,
LAST_VALUE(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS next_month_amount
FROM all_sales
WHERE year = 2003
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT PREVIOUS_MONTH_AMOUNT NEXT_MONTH_AMOUNT
---------- ------------ --------------------- -----------------
1 95525.55 95525.55 116671.6
2 116671.6 95525.55 160307.92
3 160307.92 116671.6 175998.8
4 175998.8 160307.92 154349.44
5 154349.44 175998.8 124951.36
6 124951.36 154349.44 170296.16
7 170296.16 124951.36 212735.68
8 212735.68 170296.16 199609.68
9 199609.68 212735.68 264480.79
10 264480.79 199609.68 160221.98
11 160221.98 264480.79 137336.17
12 137336.17 160221.98 137336.17
1he next query dvdes the current month's sues umount by the prevous month's sues umount
(ubeed us curr_div_prev) und uso dvdes the current month's sues umount by the next
month's sues umount (ubeed us curr_div_next):
SELECT
month, SUM(amount) AS month_amount,
SUM(amount)/FIRST_VALUE(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS curr_div_prev,
SUM(amount)/LAST_VALUE(SUM(amount)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS curr_div_next
FROM all_sales
WHERE year = 2003
230
Cruce Dutubuse ll SQL
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT CURR_DIV_PREV CURR_DIV_NEXT
---------- ------------ ------------- -------------
1 95525.55 1 .818755807
2 116671.6 1.22136538 .727796855
3 160307.92 1.37400978 .910846665
4 175998.8 1.09787963 1.14026199
5 154349.44 .876991434 1.23527619
6 124951.36 .809535558 .733729756
7 170296.16 1.36289961 .800505867
8 212735.68 1.24921008 1.06575833
9 199609.68 .93829902 .754722791
10 264480.79 1.3249898 1.65071478
11 160221.98 .605798175 1.16664081
12 137336.17 .857161858 1
1hs concudes the dscusson ol wndow lunctons.
Using thc Rcpurting functiuns
You use the reportng lunctons to perlorm cucuutons ucross groups und purttons wthn groups.
You cun perlorm reportng wth the loowng lunctons: SUM(), AVG(), MAX(), MIN(),
COUNT(), VARIANCE(), und STDDEV(). You cun uso use the RATIO_TO_REPORT() luncton
to compute the ruto ol u vuue to the sum ol u set ol vuues.
ln ths secton, you' see how to perlorm u report on u sum und use the RATIO_TO_
REPORT() luncton.
Rcpurting un a Sum
lor the lrst three months ol 2003, the loowng query reports
1he totu sum ol u sues lor u three months (ubeed us total_month_amount).
1he totu sum ol u sues lor u product types (ubeed us total_product_type_
amount).
SELECT
month, prd_type_id,
SUM(SUM(amount)) OVER (PARTITION BY month)
AS total_month_amount,
SUM(SUM(amount)) OVER (PARTITION BY prd_type_id)
AS total_product_type_amount
FROM all_sales
WHERE year = 2003
AND month <= 3
GROUP BY month, prd_type_id
ORDER BY month, prd_type_id;
MONTH PRD_TYPE_ID TOTAL_MONTH_AMOUNT TOTAL_PRODUCT_TYPE_AMOUNT
---------- ----------- ------------------ -------------------------
1 1 95525.55 201303.92
1 2 95525.55 44503.92
Chupter 7: Advunced Queres
231
1 3 95525.55 61003.92
1 4 95525.55 65693.31
1 5 95525.55
2 1 116671.6 201303.92
2 2 116671.6 44503.92
2 3 116671.6 61003.92
2 4 116671.6 65693.31
2 5 116671.6
3 1 160307.92 201303.92
3 2 160307.92 44503.92
3 3 160307.92 61003.92
3 4 160307.92 65693.31
3 5 160307.92
Notce thut the query uses the loowng expresson to report the totu sum ol u sues lor u
months (ubeed us total_month_amount):
SUM(SUM(amount)) OVER (PARTITION BY month)
AS total_month_amount
Let's breuk down ths expresson:
SUM(amount) computes the sum ol un umount. 1he outer SUM() computes the totu sum.
OVER (PARTITION BY month) cuuses the outer SUM() to compute the sum lor
euch month.
1he prevous query uso uses the loowng expresson to report the totu sum ol u sues lor
u product types (ubeed us total_product_type_amount):
SUM(SUM(amount)) OVER (PARTITION BY prd_type_id)
AS total_product_type_amount
Let's breuk down ths expresson:
SUM(amount) computes the sum ol un umount. 1he outer SUM() computes the totu sum.
OVER (PARTITION BY prd_type_id) cuuses the outer SUM() to compute the sum
lor euch product type.
Using thc RATlO_TO_RfPORT() functiun
You use the RATIO_TO_REPORT() luncton to compute the ruto ol u vuue to the sum ol u set
ol vuues.
lor the lrst three months ol 2003, the loowng query reports
1he sum ol the sues umount by product type lor euch month (ubeed us prd_type_
amount).
1he ruto ol the product type's sues umount to the entre month's sues (ubeed us prd_
type_ratio), whch s computed usng RATIO_TO_REPORT().
SELECT
month, prd_type_id,
SUM(amount) AS prd_type_amount,
RATIO_TO_REPORT(SUM(amount)) OVER (PARTITION BY month) AS prd_type_ratio
232
Cruce Dutubuse ll SQL
FROM all_sales
WHERE year = 2003
AND month <= 3
GROUP BY month, prd_type_id
ORDER BY month, prd_type_id;
MONTH PRD_TYPE_ID PRD_TYPE_AMOUNT PRD_TYPE_RATIO
---------- ----------- --------------- --------------
1 1 38909.04 .40731553
1 2 14309.04 .149792804
1 3 24909.04 .260757881
1 4 17398.43 .182133785
1 5
2 1 70567.9 .604842138
2 2 13367.9 .114577155
2 3 15467.9 .132576394
2 4 17267.9 .148004313
2 5
3 1 91826.98 .57281624
3 2 16826.98 .104966617
3 3 20626.98 .128670998
3 4 31026.98 .193546145
3 5
Notce thut the query uses the loowng expresson to compute the ruto (ubeed us prd_
type_ratio):
RATIO_TO_REPORT(SUM(amount)) OVER (PARTITION BY month) AS prd_type_ratio
Let's breuk down ths expresson:
SUM(amount) computes the sum ol the sues umount.
OVER (PARTITION BY month) cuuses the outer SUM() to compute the sum ol the
sues umount lor euch month.
1he ruto s computed by dvdng the sum ol the sues umount lor euch product type by
the sum ol the entre month's sues umount.
1hs concudes the dscusson ol reportng lunctons.
Using thc lAG() and lfAD() functiuns
You use the LAG() und LEAD() lunctons to get u vuue n u row where thut row s u certun
number ol rows uwuy lrom the current row. 1he loowng query uses LAG() und LEAD() to
get the prevous und next month's sues umount:
SELECT
month, SUM(amount) AS month_amount,
LAG(SUM(amount), 1) OVER (ORDER BY month) AS previous_month_amount,
LEAD(SUM(amount), 1) OVER (ORDER BY month) AS next_month_amount
FROM all_sales
WHERE year = 2003
Chupter 7: Advunced Queres
233
GROUP BY month
ORDER BY month;
MONTH MONTH_AMOUNT PREVIOUS_MONTH_AMOUNT NEXT_MONTH_AMOUNT
---------- ------------ --------------------- -----------------
1 95525.55 116671.6
2 116671.6 95525.55 160307.92
3 160307.92 116671.6 175998.8
4 175998.8 160307.92 154349.44
5 154349.44 175998.8 124951.36
6 124951.36 154349.44 170296.16
7 170296.16 124951.36 212735.68
8 212735.68 170296.16 199609.68
9 199609.68 212735.68 264480.79
10 264480.79 199609.68 160221.98
11 160221.98 264480.79 137336.17
12 137336.17 160221.98
Notce thut the query uses the loowng expressons to get the prevous und next month's sues:
LAG(SUM(amount), 1) OVER (ORDER BY month) AS previous_month_amount,
LEAD(SUM(amount), 1) OVER (ORDER BY month) AS next_month_amount
LAG(SUM(amount), 1) gets the prevous row's sum ol the umount. LEAD(SUM(amount),
1) gets the next row's sum ol the umount.
Using thc flRST and lAST functiuns
You use the FIRST und LAST lunctons to get the lrst und ust vuues n un ordered group. You cun
use FIRST und LAST wth the loowng lunctons: MIN(), MAX(), COUNT(), SUM(), AVG(),
STDDEV(), und VARIANCE().
1he loowng query uses FIRST und LAST to get the months n 2003 thut hud the hghest
und owest sues:
SELECT
MIN(month) KEEP (DENSE_RANK FIRST ORDER BY SUM(amount))
AS highest_sales_month,
MIN(month) KEEP (DENSE_RANK LAST ORDER BY SUM(amount))
AS lowest_sales_month
FROM all_sales
WHERE year = 2003
GROUP BY month
ORDER BY month;
HIGHEST_SALES_MONTH LOWEST_SALES_MONTH
------------------- ------------------
1 10
Using thc lincar Rcgrcssiun functiuns
You use the neur regresson lunctons to lt un ordnury-eust-squures regresson ne to u set ol
number purs. You cun use the neur regresson lunctons us uggregute, wndowng, or reportng
234
Cruce Dutubuse ll SQL
lunctons. 1he loowng tube shows the neur regresson lunctons. ln the luncton syntux, y s
nterpreted by the lunctons us u vurube thut depends on x.
functiun Dcscriptiun
REGR_AVGX(y, x)
Returns the uveruge ol x ulter emnutng x und y purs where
ether x or y s nu
REGR_AVGY(y, x)
Returns the uveruge ol y ulter emnutng x und y purs where
ether x or y s nu
REGR_COUNT(y, x)
Returns the number ol non-nu number purs thut ure used to
lt the regresson ne
REGR_INTERCEPT(y, x)
Returns the ntercept on the y-uxs ol the regresson ne
REGR_R2(y, x)
Returns the coellcent ol determnuton (R-squured) ol the
regresson ne
REGR_SLOPE(y, x)
Returns the sope ol the regresson ne
REGR_SXX(y, x)
Returns REG_COUNT (y, x) * VAR_POP(x)
REGR_SXY(y, x)
Returns REG_COUNT (y, x) * COVAR_POP(y, x)
REGR_SYY(y, x)
Returns REG_COUNT (y, x) * VAR_POP (y)
1he loowng query shows the use ol the neur regresson lunctons:
SELECT
prd_type_id,
REGR_AVGX(amount, month) AS avgx,
REGR_AVGY(amount, month) AS avgy,
REGR_COUNT(amount, month) AS count,
REGR_INTERCEPT(amount, month) AS inter,
REGR_R2(amount, month) AS r2,
REGR_SLOPE(amount, month) AS slope,
REGR_SXX(amount, month) AS sxx,
REGR_SXY(amount, month) AS sxy,
REGR_SYY(amount, month) AS syy
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id;
PRD_TYPE_ID AVGX AVGY COUNT INTER R2
----------- ---------- ---------- ---------- ---------- ----------
SLOPE SXX SXY SYY
---------- ---------- ---------- ----------
1 6.5 12570.5811 72 13318.4543 .003746289
-115.05741 858 -98719.26 3031902717
2 6.5 2588.62806 72 2608.11268 .0000508
-2.997634 858 -2571.97 151767392
3 6.5 6642.65153 72 2154.23119 .126338815
Chupter 7: Advunced Queres
23S
690.526206 858 592471.485 3238253324
4 6.5 5593.76611 72 2043.47164 .128930297
546.199149 858 468638.87 1985337488
5 0
Using thc HyputhcticaI Rank and Distributiun functiuns
You use the hypothetcu runk und dstrbuton lunctons to cucuute the runk und percente thut u
new row woud huve l you nserted t nto u tube. You cun perlorm hypothetcu cucuutons wth
the loowng lunctons: RANK(), DENSE_RANK(), PERCENT_RANK(), und CUME_DIST().
An exumpe ol u hypothetcu luncton w be gven ulter the loowng query, whch uses
RANK() und PERCENT_RANK() to get the runk und percent runk ol sues by product type lor 2003:
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC) AS rank,
PERCENT_RANK() OVER (ORDER BY SUM(amount) DESC) AS percent_rank
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id
ORDER BY prd_type_id;
PRD_TYPE_ID SUM(AMOUNT) RANK PERCENT_RANK
----------- ----------- ---------- ------------
1 905081.84 1 0
2 186381.22 4 1
3 478270.91 2 .333333333
4 402751.16 3 .666666667
1he next query shows the hypothetcu runk und percent runk ol u sues umount ol S500,000:
SELECT
RANK(500000) WITHIN GROUP (ORDER BY SUM(amount) DESC)
AS rank,
PERCENT_RANK(500000) WITHIN GROUP (ORDER BY SUM(amount) DESC)
AS percent_rank
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id
ORDER BY prd_type_id;
RANK PERCENT_RANK
---------- ------------
2 .25
As you cun see, the hypothetcu runk und percent runk ol u sues umount ol S500,000 ure 2
und .25.
1hs concudes the dscusson ol hypothetcu lunctons.
236
Cruce Dutubuse ll SQL
Using thc MODfl CIausc
1he MODEL cuuse wus ntroduced wth Cruce Dutubuse l0 und enubes you to perlorm nter-
row cucuutons. 1he MODEL cuuse uows you to uccess u coumn n u row ke u ce n un
urruy. 1hs gves you the ubty to perlorm cucuutons n u smur munner to spreudsheet
cucuutons. lor exumpe, the all_sales tube contuns sues nlormuton lor the months n
2003. You cun use the MODEL cuuse to cucuute sues n luture months bused on sues n 2003.
An fxampIc uf thc MODfl CIausc
1he eusest wuy to eurn how to use the MODEL cuuse s to see un exumpe. 1he loowng query
retreves the sues umount lor euch month n 2003 mude by empoyee r2l lor product types rl
und r2 und computes the predcted sues lor }unuury, lebruury, und Murch ol 2004 bused on sues
n 2003:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[1, 2004] = sales_amount[1, 2003],
sales_amount[2, 2004] = sales_amount[2, 2003] + sales_amount[3, 2003],
sales_amount[3, 2004] = ROUND(sales_amount[3, 2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
Let's breuk down ths query:
PARTITION BY (prd_type_id) specles thut the resuts ure purttoned by prd_
type_id.
DIMENSION BY (month, year) specles thut the dmensons ol the urruy ure month
und year. 1hs meuns thut u ce n the urruy s uccessed by speclyng u month und yeur.
MEASURES (amount sales_amount) specles thut euch ce n the urruy contuns
un umount und thut the urruy nume s sales_amount. 1o uccess the ce n the sales_
amount urruy lor }unuury 2003, you use sales_amount[1, 2003], whch returns the
sues umount lor thut month und yeur.
Alter MEASURES come three nes thut compute the luture sues lor }unuury, lebruury, und
Murch ol 2004:
sales_amount[1, 2004] = sales_amount[1, 2003] sets the sues umount
lor }unuury 2004 to the umount lor }unuury 2003.
sales_amount[2, 2004] = sales_amount[2, 2003] + sales_
amount[3, 2003] sets the sues umount lor lebruury 2004 to the umount lor
lebruury 2003 pus Murch 2003.
Chupter 7: Advunced Queres
237
sales_amount[3, 2004] = ROUND(sales_amount[3, 2003] * 1.25,
2) sets the sues umount lor Murch 2004 to the rounded vuue ol the sues umount lor
Murch 2003 mutped by l.25.
ORDER BY prd_type_id, year, month smpy orders the resuts returned by the
entre query.
1he output lrom the query s shown n the loowng stng, notce thut the resuts contun the
sues umounts lor u months n 2003 lor product types rl und r2, pus the predcted sues
umounts lor the lrst three months n 2004 (whch l've mude bod to muke them stund out):
PRD_TYPE_ID YEAR MONTH SALES_AMOUNT
----------- ---------- ---------- ------------
1 2003 1 10034.84
1 2003 2 15144.65
1 2003 3 20137.83
1 2003 4 25057.45
1 2003 5 17214.56
1 2003 6 15564.64
1 2003 7 12654.84
1 2003 8 17434.82
1 2003 9 19854.57
1 2003 10 21754.19
1 2003 11 13029.73
1 2003 12 10034.84
1 2004 1 10034.84
1 2004 2 35282.48
1 2004 3 25172.29
2 2003 1 1034.84
2 2003 2 1544.65
2 2003 3 2037.83
2 2003 4 2557.45
2 2003 5 1714.56
2 2003 6 1564.64
2 2003 7 1264.84
2 2003 8 1734.82
2 2003 9 1854.57
2 2003 10 2754.19
2 2003 11 1329.73
2 2003 12 1034.84
2 2004 1 1034.84
2 2004 2 3582.48
2 2004 3 2547.29
Using PusitiunaI and SymbuIic Nutatiun tu Acccss CcIIs
ln the prevous exumpe, you suw how to uccess u ce n un urruy usng the loowng notuton:
sales_amount[1, 2004], where l s the month und 2004 s the yeur. 1hs s relerred to us
postonu notuton becuuse the meunng ol the dmensons s determned by ther poston: the
lrst poston contuns the month und the second poston contuns the yeur.
238
Cruce Dutubuse ll SQL
You cun uso use symboc notuton to expcty ndcute the meunng ol the dmensons, us
n, lor exumpe, sales_amount[month=1, year=2004]. 1he loowng query rewrtes the
prevous query to use symboc notuton:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[month=1, year=2004] = sales_amount[month=1, year=2003],
sales_amount[month=2, year=2004] =
sales_amount[month=2, year=2003] + sales_amount[month=3, year=2003],
sales_amount[month=3, year=2004] =
ROUND(sales_amount[month=3, year=2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
\hen usng postonu or symboc notuton, t s mportunt to be uwure ol the dllerent wuy
they hunde nu vuues n the dmensons. lor exumpe, sales_amount[null, 2003] returns
the umount whose month s nu und yeur s 2003, but sales_amount[month=null,
year=2004] won't uccess u vud ce becuuse null=null uwuys returns luse.
Acccssing a Rangc uf CcIIs Using BfTWffN and AND
You cun uccess u runge ol ces usng the BETWEEN und AND keywords. lor exumpe, the
loowng expresson sets the sues umount lor }unuury 2004 to the rounded uveruge ol
the sues between }unuury und Murch ol 2003:
sales_amount[1, 2004] =
ROUND(AVG(sales_amount)[month BETWEEN 1 AND 3, 2003], 2)
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[1, 2004] =
ROUND(AVG(sales_amount)[month BETWEEN 1 AND 3, 2003], 2)
)
ORDER BY prd_type_id, year, month;
Acccssing AII CcIIs Using ANY and lS ANY
You cun uccess u ces n un urruy usng the ANY und IS ANY predcutes. You use ANY wth
postonu notuton und IS ANY wth symboc notuton. lor exumpe, the loowng expresson
sets the sues umount lor }unuury 2004 to the rounded sum ol the sues lor u months und yeurs:
Chupter 7: Advunced Queres
239
sales_amount[1, 2004] =
ROUND(SUM(sales_amount)[ANY, year IS ANY], 2)
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[1, 2004] =
ROUND(SUM(sales_amount)[ANY, year IS ANY], 2)
)
ORDER BY prd_type_id, year, month;
Gctting thc Currcnt VaIuc uf a Dimcnsiun
Using CURRfNTV()
You cun get the current vuue ol u dmenson usng the CURRENTV() luncton. lor exumpe, the
loowng expresson sets the sues umount lor the lrst month ol 2004 to l.25 tmes the sues ol
the sume month n 2003, notce the use ol CURRENTV() to get the current month, whch s l:
sales_amount[1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
1he output lrom ths query s us loows (l've hghghted the vuues lor 2004 n bod):
PRD_TYPE_ID YEAR MONTH SALES_AMOUNT
----------- ---------- ---------- ------------
1 2003 1 10034.84
1 2003 2 15144.65
1 2003 3 20137.83
1 2003 4 25057.45
1 2003 5 17214.56
1 2003 6 15564.64
1 2003 7 12654.84
240
Cruce Dutubuse ll SQL
1 2003 8 17434.82
1 2003 9 19854.57
1 2003 10 21754.19
1 2003 11 13029.73
1 2003 12 10034.84
1 2004 1 12543.55
2 2003 1 1034.84
2 2003 2 1544.65
2 2003 3 2037.83
2 2003 4 2557.45
2 2003 5 1714.56
2 2003 6 1564.64
2 2003 7 1264.84
2 2003 8 1734.82
2 2003 9 1854.57
2 2003 10 2754.19
2 2003 11 1329.73
2 2003 12 1034.84
2 2004 1 1293.55
Acccssing CcIIs Using a fOR luup
You cun uccess ces usng u FOR oop. lor exumpe, the loowng expresson sets the sues
umount lor the lrst three months ol 2004 to l.25 tmes the sues ol the sume months n 2003,
notce the use ol the FOR oop und the INCREMENT keyword thut specles the umount to
ncrement month by durng euch teruton ol the oop:
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
1he output lrom ths query s us loows (l've hghghted the vuues lor 2004 n bod):
PRD_TYPE_ID YEAR MONTH SALES_AMOUNT
----------- ---------- ---------- ------------
1 2003 1 10034.84
1 2003 2 15144.65
1 2003 3 20137.83
1 2003 4 25057.45
Chupter 7: Advunced Queres
241
1 2003 5 17214.56
1 2003 6 15564.64
1 2003 7 12654.84
1 2003 8 17434.82
1 2003 9 19854.57
1 2003 10 21754.19
1 2003 11 13029.73
1 2003 12 10034.84
1 2004 1 12543.55
1 2004 2 18930.81
1 2004 3 25172.29
2 2003 1 1034.84
2 2003 2 1544.65
2 2003 3 2037.83
2 2003 4 2557.45
2 2003 5 1714.56
2 2003 6 1564.64
2 2003 7 1264.84
2 2003 8 1734.82
2 2003 9 1854.57
2 2003 10 2754.19
2 2003 11 1329.73
2 2003 12 1034.84
2 2004 1 1293.55
2 2004 2 1930.81
2 2004 3 2547.29
HandIing NuII and Missing VaIucs
ln ths secton, you' eurn how to hunde nu und mssng vuues usng the MODEL cuuse.
Using lS PRfSfNT
IS PRESENT returns true l the row specled by the ce relerence exsted pror to the executon
ol the MODEL cuuse. lor exumpe:
sales_amount[CURRENTV(), 2003] IS PRESENT
w return true l sales_amount[CURRENTV(), 2003] exsts.
1he loowng expresson sets the sues umount lor the lrst three months ol 2004 to l.25
mutped by the sues ol the sume months n 2003:
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
CASE WHEN sales_amount[CURRENTV(), 2003] IS PRESENT THEN
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
ELSE
0
END
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
242
Cruce Dutubuse ll SQL
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
CASE WHEN sales_amount[CURRENTV(), 2003] IS PRESENT THEN
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
ELSE
0
END
)
ORDER BY prd_type_id, year, month;
1he output ol ths query s the sume us the exumpe n the prevous secton.
Using PRfSfNTV()
PRESENTV(cell, expr1, expr2) returns the expresson expr1 l the row specled by
the cell relerence exsted pror to the executon ol the MODEL cuuse. ll the row doesn't exst,
the expresson expr2 s returned. lor exumpe:
PRESENTV(sales_amount[CURRENTV(), 2003],
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2), 0)
w return the rounded sues umount l sales_amount[CURRENTV(), 2003] exsts,
otherwse 0 w be returned.
1he loowng query shows the use ol ths expresson:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
PRESENTV(sales_amount[CURRENTV(), 2003],
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2), 0)
)
ORDER BY prd_type_id, year, month;
Using PRfSfNTNNV()
PRESENTNNV(cell, expr1, expr2) returns the expresson expr1 l the row specled by the
cell relerence exsted pror to the executon ol the MODEL cuuse und the ce vuue s not nu.
ll the row doesn't exst or the ce vuue s nu, the expresson expr2 s returned. lor exumpe,
PRESENTNNV(sales_amount[CURRENTV(), 2003],
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2), 0)
Chupter 7: Advunced Queres
243
w return the rounded sues umount l sales_amount[CURRENTV(), 2003] exsts und s not
nu, otherwse 0 w be returned.
Using lGNORf NAV and kffP NAV
IGNORE NAV returns
0 lor nu or mssng numerc vuues.
An empty strng lor nu or mssng strng vuues.
0l-}AN-2000 lor nu or mssng dute vuues.
Nu lor u other dutubuse types.
KEEP NAV returns nu lor nu or mssng numerc vuues. e uwure thut KEEP NAV s the
deluut.
1he loowng query shows the use ol IGNORE NAV:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL IGNORE NAV
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount) (
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
Updating fxisting CcIIs
y deluut, l the ce relerenced on the elt sde ol un expresson exsts, then t s upduted. ll the
ce doesn't exst, then u new row n the urruy s creuted. You cun chunge ths deluut behuvor
usng RULES UPDATE, whch specles thut l the ce doesn't exst, u new row w not be creuted.
1he loowng query shows the use ol RULES UPDATE:
SELECT prd_type_id, year, month, sales_amount
FROM all_sales
WHERE prd_type_id BETWEEN 1 AND 2
AND emp_id = 21
MODEL
PARTITION BY (prd_type_id)
DIMENSION BY (month, year)
MEASURES (amount sales_amount)
RULES UPDATE (
sales_amount[FOR month FROM 1 TO 3 INCREMENT 1, 2004] =
ROUND(sales_amount[CURRENTV(), 2003] * 1.25, 2)
)
ORDER BY prd_type_id, year, month;
244
Cruce Dutubuse ll SQL
ecuuse ces lor 2004 don't exst und RULES UPDATE s used, no new rows ure creuted n
the urruy lor 2004, therelore, the query doesn't return rows lor 2004. 1he loowng stng shows
the output lor the querynotce there ure no rows lor 2004:
PRD_TYPE_ID YEAR MONTH SALES_AMOUNT
----------- ---------- ---------- ------------
1 2003 1 10034.84
1 2003 2 15144.65
1 2003 3 20137.83
1 2003 4 25057.45
1 2003 5 17214.56
1 2003 6 15564.64
1 2003 7 12654.84
1 2003 8 17434.82
1 2003 9 19854.57
1 2003 10 21754.19
1 2003 11 13029.73
1 2003 12 10034.84
2 2003 1 1034.84
2 2003 2 1544.65
2 2003 3 2037.83
2 2003 4 2557.45
2 2003 5 1714.56
2 2003 6 1564.64
2 2003 7 1264.84
2 2003 8 1734.82
2 2003 9 1854.57
2 2003 10 2754.19
2 2003 11 1329.73
2 2003 12 1034.84
Using thc PlVOT and UNPlVOT CIauscs
1he PIVOT cuuse s new lor Cruce Dutubuse ll und enubes you to rotute rows nto coumns
n the output lrom u query, und, ut the sume tme, to run un uggreguton luncton on the dutu.
Cruce Dutubuse ll uso hus un UNPIVOT cuuse thut rotutes coumns nto rows n the output
lrom u query.
PIVOT und UNPIVOT ure uselu to see overu trends n urge umounts ol dutu, such us trends
n sues over u perod ol tme. You' see queres thut show the use ol PIVOT und UNPIVOT n the
loowng sectons.
A SimpIc fxampIc uf thc PlVOT CIausc
1he eusest wuy to eurn how to use the PIVOT cuuse s to see un exumpe. 1he loowng query
shows the totu sues umount ol product types rl, r2, und r3 lor the lrst lour months n 2003,
notce thut the ces n the query's output show the sum ol the sues umounts lor euch product
type n euch month:
Chupter 7: Advunced Queres
SELECT *
FROM (
SELECT month, prd_type_id, amount
FROM all_sales
WHERE year = 2003
AND prd_type_id IN (1, 2, 3)
)
PIVOT (
SUM(amount) FOR month IN (1 AS JAN, 2 AS FEB, 3 AS MAR, 4 AS APR)
)
ORDER BY prd_type_id;
PRD_TYPE_ID JAN FEB MAR APR
----------- ---------- ---------- ---------- ----------
1 38909.04 70567.9 91826.98 120344.7
2 14309.04 13367.9 16826.98 15664.7
3 24909.04 15467.9 20626.98 23844.7
Sturtng wth the lrst ne ol output, you cun see there wus
S38,909.04 ol product type rl sod n }unuury.
S70,567.90 ol product type rl sod n lebruury.
.und so on lor the rest ol the lrst ne.
1he second ne ol output shows there wus
Sl4,309.04 ol product type r2 sod n }unuury.
Sl3,367.90 ol product type r2 sod n lebruury.
.und so on lor the rest ol the output.
Lsers, lrveges,
und Roes
275
276
Cruce Dutubuse llg SQL
n ths chupter, you w do the loowng:
Leurn more ubout users
See how prveges ure used to enube users to perlorm tusks n the dutubuse
Lxpore the two types ol prveges: system prveges und obect prveges
Leurn how system prveges uow you to perlorm uctons such us executng DDL
stutements
See how obect prveges uow you to perlorm uctons such us executng DML stutements
Lxpore how to group prveges together nto roes
Leurn how to uudt the executon ol SQL stutements
NOTf
You' nccd |o |,pc n |hc S |a|cmcn| hovn n |h chap|cr |
,ou van| |o |oov |hc cxampc: Thc |a|cmcn| arc no| con|ancd
n an, crp|.
Uscrs
ln ths secton, you' eurn how to creute u user, uter u user's pussword, und drop u user.
You w see the term tubespuce used n ths chupter. 1ubespuces ure used by the dutubuse
to store sepurute obects, whch cun ncude tubes, types, lL/SQL code, und so on. 1ypcuy,
reuted obects ure grouped together und stored n the sume tubespuce. lor exumpe, you mght
creute un order entry uppcuton und store u the obects lor thut uppcuton n one tubespuce,
und you mght creute u suppy chun uppcuton und store the obects lor thut uppcuton n u
dllerent tubespuce. lor more detus on tubespuces, you shoud reud the Cracc Da|abac
Conccp| munuu pubshed by Cruce Corporuton.
Crcating a Uscr
1o creute u user n the dutubuse, you use the CREATE USER stutement. 1he smpled syntux lor
the CREATE USER stutement s us loows:
CREATE USER user_name IDENTIFIED BY password
[DEFAULT TABLESPACE default_tablespace]
[TEMPORARY TABLESPACE temporary_tablespace];
where
user_name s the nume ol the dutubuse user.
password s the pussword lor the dutubuse user.
default_tablespace s the deluut tubespuce where dutubuse obects ure stored. ll
you omt u deluut tubespuce, the deluut SYSTEM tubespuce, whch uwuys exsts n u
dutubuse, s used.
ON COMMIT contros the duruton ol the rows n u temporury tube. DELETE meuns the
rows ure deeted ut the end ol u trunsucton. PRESERVE meuns the rows ure kept unt the
end ol u user sesson, ut whch pont the rows ure deeted. ll you omt ON COMMIT lor u
temporury tube, then the deluut DELETE s used.
tab_space s the tubespuce lor the tube. ll you omt u tubespuce, then the tube s
stored n the user's deluut tubespuce.
Thc |u CREATE TABLE ,n|ax |ar rchcr |han |ha| hovn abovc. or
|u dc|a, cc |hc Cruce Dutubuse SQL Relerence boo| pubhcd
b, Cracc Corpora|on.
1he loowng exumpe connects us the store user und creutes u tube numed order_
status2:
CONNECT store/store_password
CREATE TABLE order_status2 (
id INTEGER CONSTRAINT order_status2_pk PRIMARY KEY,
status VARCHAR2(10),
last_modified DATE DEFAULT SYSDATE
);
Any subsequent chunges mude to the store.products tube ure now recorded n the urchve.
1he loowng INSERT stutement udds u row to the store.products tube:
INSERT INTO store.products (
product_id, product_type_id, name, description, price
) VALUES (
15, 1, 'Using Linux', 'How to Use Linux', 39.99
);
1he loowng query retreves ths row:
SELECT product_id, name, price
FROM store.products
WHERE product_id = 15;
PRODUCT_ID NAME PRICE
---------- ------------------------------ ----------
15 Using Linux 39.99
You cun vew the rows us they were 5 mnutes ugo usng the loowng query:
SELECT product_id, name, price
FROM store.products
AS OF TIMESTAMP
(SYSTIMESTAMP - INTERVAL '5' MINUTE);
PRODUCT_ID NAME PRICE
---------- ------------------------------ ----------
1 Modern Science 19.95
2 Chemistry 30
3 Supernova 25.99
4 Tank War 13.95
5 Z Files 49.99
6 2412: The Return 14.95
7 Space Force 9 13.49
8 From Another Planet 12.99
9 Classical Music 10.99
10 Pop 3 15.99
11 Creative Yell 14.99
12 My Front Line 13.49
13 Western Front 13.5
14 Eastern Front 16.5
Notce thut the new row s mssng. 1hs s becuuse t wus udded to the tube ulter the dute
und tme specled n the query (ussumng the prevous INSERT wus run ess thun 5 mnutes ugo).
You cun uso vew the rows us they were ut u speclc tmestump usng the loowng query (l
you run ths query, you need to chunge the tmestump to u dute und tme belore you run the INSERT
stutement eurer):
SELECT product_id, name, price
FROM store.products
AS OF TIMESTAMP
TO_TIMESTAMP('2007-08-12 13:05:00', 'YYYY-MM-DD HH24:MI:SS');
Cruce Dutubuse ll SQL
1he new row w be mssng lrom the resuts ugun, becuuse t wus udded to the tube ulter
the dute und tme specled n the query.
You cun vew the rows us they were between two tmestumps usng the loowng query (you
need to chunge the tmestumps):
SELECT product_id, name, price
FROM store.products VERSIONS BETWEEN TIMESTAMP
TO_TIMESTAMP('2007-08-12 12:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_TIMESTAMP('2007-08-12 12:59:59', 'YYYY-MM-DD HH24:MI:SS');
You cun vew the rows us they were between one tmestump und the present tme usng the
loowng query (you need to chunge the tmestump):
SELECT product_id, name, price
FROM store.products VERSIONS BETWEEN TIMESTAMP
TO_TIMESTAMP('2007-08-12 13:45:52', 'YYYY-MM-DD HH24:MI:SS')
AND MAXVALUE;
You cun stop urchvng ol dutu lor u tube usng ALTER TABLE, lor exumpe:
ALTER TABLE store.products NO FLASHBACK ARCHIVE;
\hen you creute u tube, you cun specly u lushbuck urchve lor thut tube, lor exumpe:
CREATE TABLE store.test_table (
id INTEGER,
name VARCHAR2(10)
) FLASHBACK ARCHIVE test_archive;
You cun vew the detus lor un urchve usng the loowng vews:
user_flashback_archive und dba_flashback_archive, whch dspuy generu
nlormuton ubout the lushbuck urchves
user_flashback_archive_ts und dba_flashback_archive_ts, whch dspuy
nlormuton ubout the tubespuces contunng the lushbuck urchves
user_flashback_archive_tables und dba_flashback_archive_tables,
whch dspuy nlormuton ubout the tubes thut ure enubed lor lushbuck urchvng
You cun uter u lushbuck urchve, lor exumpe, the loowng stutement chunges the dutu
retenton perod to 2 yeurs:
ALTER FLASHBACK ARCHIVE test_archive
MODIFY RETENTION 2 YEAR;
You cun purge the dutu lrom u lushbuck urchve belore u gven tmestump, lor exumpe, the
loowng stutement purges dutu oder thun l duy:
ALTER FLASHBACK ARCHIVE test_archive
PURGE BEFORE TIMESTAMP(SYSTIMESTAMP INTERVAL '1' DAY);
Chupter l0: Creutng 1ubes, Sequences, lndexes, und Vews
337
You cun purge u the dutu n u lushbuck urchve, lor exumpe:
ALTER FLASHBACK ARCHIVE test_archive PURGE ALL;
You cun drop u lushbuck urchve, lor exumpe:
DROP FLASHBACK ARCHIVE test_archive;
NOTf
Co ahcad and rcrun store_schema.sql |o rc-crca|c |hc |orc
|abc o |ha| ,our qucrc ma|ch mnc n |hc rc| o| |h boo|.
Summary
ln ths chupter, you huve eurned the loowng:
A tube s creuted usng the CREATE TABLE stutement.
A sequence generutes u sequence ol ntegers.
A dutubuse ndex cun speed up uccess to rows.
A vew s u predelned query on one or more buse tubes.
A lushbuck dutu urchve stores chunges mude to u tube over u perod ol tme.
ln the next chupter, you' eurn ubout lL/SQL progrummng.
This page intentionally left blank
lntroducng lL/SQL
lrogrummng
339
340
Cruce Dutubuse llg SQL
ruce udded u proceduru progrummng unguuge known us lL/SQL (lroceduru
Lunguuge/SQL) to Cruce Dutubuse 6. lL/SQL enubes you to wrte progrums thut
contun SQL stutements. ln ths chupter, you' eurn ubout the loowng lL/SQL
topcs:
ock structure
Vurubes und types
Condtonu ogc
Loops
Cursors, whch uow lL/SQL to reud the resuts returned by u query
lrocedures
lunctons
luckuges, whch ure used to group procedures und lunctons together n one unt
1rggers, whch ure bocks ol code thut ure run when u certun event occurs n the
dutubuse
Cruce Dutubuse llg enhuncements to lL/SQL
You cun use lL/SQL to udd busness ogc to u dutubuse uppcuton. 1hs centruzed busness
ogc cun be run by uny progrum thut cun uccess the dutubuse, ncudng SQL*lus, }uvu progrums,
Cr progrums, und more.
NOTf
or |u dc|a on hov |o accc a da|abac |hrough }ava, cc m, boo|
Cruce9 }DC lrogrummng (Cracc rc, 2002). or dc|a on hov
|o accc a da|abac |hrough C~, cc m, boo| Musterng Cr Dutubuse
lrogrummng (S,bcx, 200J).
BIuck Structurc
lL/SQL progrums ure dvded up nto structures known us boc|, wth euch bock contunng lL/
SQL und SQL stutements. A lL/SQL bock hus the loowng structure:
[DECLARE
declaration_statements
]
BEGIN
executable_statements
[EXCEPTION
exception_handling_statements
]
END;
/
where
declaration_statements decure the vurubes used n the rest ol the lL/SQL bock.
DECLARE bocks ure optonu.
executable_statements ure the uctuu executube stutements, whch muy ncude
oops, condtonu ogc, und so on.
exception_handling_statements ure stutements thut hunde uny executon errors
thut mght occur when the bock s run. EXCEPTION bocks ure optonu.
Lvery stutement s termnuted by u semcoon (,), und u lL/SQL bock s termnuted usng the
lorwurd sush (/) churucter. elore l get nto the detus ol lL/SQL, you' see u smpe exumpe to
get u lee lor the unguuge. 1he loowng exumpe (contuned n the area_example.sql scrpt
n the SQL drectory) cucuutes the wdth ol u rectunge gven ts ureu und heght:
SET SERVEROUTPUT ON
DECLARE
v_width INTEGER;
v_height INTEGER := 2;
v_area INTEGER := 6;
BEGIN
-- set the width equal to the area divided by the height
v_width := v_area / v_height;
DBMS_OUTPUT.PUT_LINE('v_width = ' || v_width);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/
1he SET SERVEROUTPUT ON commund turns the server output on so you cun see the nes
produced by DBMS_OUTPUT.PUT_LINE() on the screen when you run the scrpt n SQL*lus.
Alter ths ntu commund comes the lL/SQL bock tsel, whch s dvded nto the DECLARE,
BEGIN, und EXCEPTION bocks.
1he DECLARE bock contuns decurutons lor three INTEGER vurubes numed v_width, v_
height, und v_area (l uwuys put v_ ut the sturt ol vurube numes). 1he v_height und v_
area vurubes ure ntuzed to 2 und 6 respectvey.
Next comes the BEGIN bock, whch contuns three nes. 1he lrst ne s u comment thut
contuns the text set the wdth equu to the ureu dvded by the heght. 1he second ne sets v_
width to v_area dvded by v_height, ths meuns v_width s set to 3 (= 6 / 2). 1he thrd ne
cus DBMS_OUTPUT.PUT_LINE() to dspuy the vuue ol v_width on the screen. DBMS_OUTPUT
s u but-n puckuge ol code thut comes wth the Cruce dutubuse, umong other tems, DBMS_
OUTPUT contuns procedures thut uow you to output vuues to the screen.
Next, the EXCEPTION bock hundes uny uttempts to dvde u number by zero. lt does ths by
cutchng the ZERO_DIVIDE excepton, n the exumpe, no uttempt s uctuuy mude to dvde
by zero, but l you chunge v_height to 0 und run the scrpt you' see the excepton.
At the very end ol the scrpt, the lorwurd sush churucter (/) murks the end ol the lL/SQL bock.
342
Cruce Dutubuse llg SQL
1he loowng stng shows the executon ol the area_example.sql scrpt n SQL*lus:
SQL> @ C:\SQL\area_example.sql
v_width = 3
NOTf
| ,our area_example.sql crp| n a drcc|or, o|hcr |han
C:\SQL, uc ,our ovn drcc|or, n |hc prcvou command.
VariabIcs and Typcs
Vurubes ure decured wthn u DECLARE bock. As you suw n the prevous exumpe, u vurube
decuruton hus both u nume und u type. lor exumpe, the v_width vurube wus decured us
v_width INTEGER;
NOTf
Thc /S |,pc arc mar |o |hc da|abac coumn |,pc. You can
cc a |hc |,pc n |hc appcndx.
1he loowng exumpe shows more vurube decurutons (these vurubes coud be used to
store the coumn vuues lrom the products tube):
v_product_id INTEGER;
v_product_type_id INTEGER;
v_name VARCHAR2(30);
v_description VARCHAR2(50);
v_price NUMBER(5, 2);
You muy uso specly u vurube's type usng the %TYPE keyword, whch tes lL/SQL to use
the sume type us u specled coumn n u tube. 1he loowng exumpe uses %TYPE to decure u
vurube ol the sume type us the price coumn ol the products tube, whch s NUMBER(5, 2):
v_product_price products.price%TYPE;
CunditiunaI lugic
You use the IF, THEN, ELSE, ELSIF, und END IF keywords to perlorm condtonu ogc:
IF condition1 THEN
statements1
ELSIF condition2 THEN
statements2
ELSE
statements3
END IF;
where
condition1 und condition2 ure ooeun expressons thut evuuute to true or luse.
statements1, statements2, und statements3 ure lL/SQL stutements.
Chupter ll: lntroducng lL/SQL lrogrummng
343
1he condtonu ogc lows us loows:
ll condition1 s true, then statements1 ure executed.
ll condition1 s luse but condition2 s true, then statements2 ure executed.
ll nether condition1 nor condition2 s true, then statements3 ure executed.
You cun uso embed un IF stutement wthn unother IF stutement, us shown n the loowng
exumpe:
IF v_count > 0 THEN
v_message := 'v_count is positive';
IF v_area > 0 THEN
v_message := 'v_count and v_area are positive';
END IF
ELSIF v_count = 0 THEN
v_message := 'v_count is zero';
ELSE
v_message := 'v_count is negative';
END IF;
ln ths exumpe, l v_count s greuter thun 0, then v_message s set to 'v_count is
positive'. ll v_count und v_area ure greuter thun 0, then v_message s set to 'v_count
and v_area are positive'. 1he rest ol the ogc s strughtlorwurd.
luups
You use u oop to run stutements zero or more tmes. 1here ure three types ol oops n lL/SQL:
SimpIc Iuups run unt you expcty end the oop.
WHILE Iuups run unt u specled condton occurs.
FOR Iuups run u predetermned number ol tmes.
You' eurn ubout these oops n the loowng sectons.
SimpIc luups
A smpe oop runs unt you expcty end the oop. 1he syntux lor u smpe oop s us loows:
LOOP
statements
END LOOP;
1o end the oop, you use ether un EXIT or un EXIT WHEN stutement. 1he EXIT stutement ends
u oop mmedutey, the EXIT WHEN stutement ends u oop when u specled condton occurs.
1he loowng exumpe shows u smpe oop. A vurube numed v_counter s ntuzed to 0
pror to the begnnng ol the oop. 1he oop udds l to v_counter und exts when v_counter s
equu to 5 usng un EXIT WHEN stutement.
v_counter := 0;
LOOP
v_counter := v_counter + 1;
EXIT WHEN v_counter = 5;
END LOOP;
344
Cruce Dutubuse llg SQL
NOTf
Thc EXIT WHEN |a|cmcn| can appcar an,vhcrc n |hc oop codc.
ln Cruce Dutubuse llg you cun uso end the current teruton ol u oop usng the CONTINUE
or CONTINUE WHEN stutement. 1he CONTINUE stutement ends the current teruton ol the oop
uncondtonuy und contnues wth the next teruton, the CONTINUE WHEN stutement ends the
current teruton ol the oop when u specled condton occurs und then contnues wth the next
teruton. 1he loowng exumpe shows the use ol the CONTINUE stutement:
v_counter := 0;
LOOP
-- after the CONTINUE statement is executed, control returns here
v_counter := v_counter + 1;
IF v_counter = 3 THEN
CONTINUE; -- end current iteration unconditionally
END IF;
EXIT WHEN v_counter = 5;
END LOOP;
1he next exumpe shows the use ol the CONTINUE WHEN stutement:
v_counter := 0;
LOOP
-- after the CONTINUE WHEN statement is executed, control returns here
v_counter := v_counter + 1;
CONTINUE WHEN v_counter = 3; -- end current iteration when v_counter = 3
EXIT WHEN v_counter = 5;
END LOOP;
NOTf
A CONTINUE or CONTINUE WHEN |a|cmcn| canno| cro a
proccdurc, |unc|on, or mc|hod boundar,.
WHllf luups
A WHILE oop runs unt u specled condton occurs. 1he syntux lor u WHILE oop s us loows:
WHILE condition LOOP
statements
END LOOP;
1he loowng exumpe shows u WHILE oop thut executes whe the v_counter vurube s
ess thun 6:
v_counter := 0;
WHILE v_counter < 6 LOOP
v_counter := v_counter + 1;
END LOOP;
fOR luups
A FOR oop runs u predetermned number ol tmes, you determne the number ol tmes the oop
runs by speclyng the ovcr und uppcr bound lor u oop vurube. 1he oop vurube s then
ncremented (or decremented) euch tme uround the oop. 1he syntux lor u FOR oop s us loows:
Chupter ll: lntroducng lL/SQL lrogrummng
34S
FOR loop_variable IN [REVERSE] lower_bound..upper_bound LOOP
statements
END LOOP;
where
loop_variable s the oop vurube. You cun use u vurube thut ureudy exsts us the
oop vurube, or you cun ust huve the oop creute u new vurube lor you (ths occurs l
the vurube you specly doesn't exst). 1he oop vurube vuue s ncreused (or decreused
l you use the REVERSE keyword) by l euch tme through the oop.
REVERSE meuns thut the oop vurube vuue s to be decremented euch tme through
the oop. 1he oop vurube s ntuzed to the upper boundury, und s decremented by l
unt the oop vurube reuches the ower boundury. You must specly the ower boundury
belore the upper boundury.
lower_bound s the oop's ower boundury. 1he oop vurube s ntuzed to ths ower
boundury provded REVERSE s not used.
upper_bound s the oop's upper boundury. ll REVERSE s used, the oop vurube s
ntuzed to ths upper boundury.
1he loowng exumpe shows u FOR oop. Notce thut the vurube v_counter2 sn't expcty
decuredso the FOR oop uutomutcuy creutes u new INTEGER vurube numed v_counter2:
FOR v_counter2 IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(v_counter2);
END LOOP;
1he loowng exumpe uses REVERSE:
FOR v_counter2 IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(v_counter2);
END LOOP;
ln ths exumpe, v_counter2 sturts ut 5, s decremented by l euch tme through the oop,
und ends ut l.
Cursurs
You use u to letch rows returned by u query. You retreve the rows nto the cursor usng u
query und then letch the rows one ut u tme lrom the cursor. You typcuy use the loowng lve
steps when usng u cursor:
1. Decure vurubes to store the coumn vuues lor u row.
2. Decure the cursor, whch contuns u query.
3. Cpen the cursor.
4. letch the rows lrom the cursor one ut u tme und store the coumn vuues n the vurubes
decured n Step l. You woud then do somethng wth those vurubes, such us dspuy
them on the screen, use them n u cucuuton, und so on.
S. Cose the cursor.
346
Cruce Dutubuse llg SQL
You' eurn the detus ol these lve steps n the loowng sectons, und you' see u smpe
exumpe thut gets the product_id, name, und price coumns lrom the products tube.
Stcp 1: DccIarc thc VariabIcs tu Sturc thc CuIumn VaIucs
1he lrst step s to decure the vurubes thut w be used to store the coumn vuues. 1hese
vurubes must be computbe wth the coumn types.
TlP
arcr ,ou av |ha| %TYPE ma, bc ucd |o gc| |hc |,pc o| a coumn.
| ,ou uc %TYPE vhcn dccarng ,our varabc, ,our varabc v
au|oma|ca, bc o| |hc corrcc| |,pc.
1he loowng exumpe decures three vurubes to store the product_id, name, und price
coumns lrom the products tube, notce thut %TYPE s used to uutomutcuy set the type ol the
vurubes to the sume type us the coumns:
DECLARE
v_product_id products.product_id%TYPE;
v_name products.name%TYPE;
v_price products.price%TYPE;
Stcp 2: DccIarc thc Cursur
Step 2 s to decure the cursor. A cursor decuruton conssts ol u nume thut you ussgn to the cursor
und the query you wunt to execute. 1he cursor decuruton, ke u other decurutons, s puced n
the decuruton secton. 1he syntux lor decurng u cursor s us loows:
CURSOR cursor_name IS
SELECT_statement;
where
cursor_name s the nume ol the cursor.
SELECT_statement s the query.
1he loowng exumpe decures u cursor numed v_product_cursor whose query retreves
the product_id, name, und price coumns lrom the products tube:
CURSOR v_product_cursor IS
SELECT product_id, name, price
FROM products
ORDER BY product_id;
1he query sn't executed unt you open the cursor.
Stcp 3: Opcn thc Cursur
Step 3 s to open the cursor. You open u cursor usng the OPEN stutement, whch must be puced
n the executube secton ol the bock.
1he loowng exumpe opens v_product_cursor, whch executes the query:
OPEN v_product_cursor;
Chupter ll: lntroducng lL/SQL lrogrummng
347
Stcp 4: fctch thc Ruws frum thc Cursur
Step 4 s to letch the rows lrom the cursor, whch you do usng the FETCH stutement. 1he FETCH
stutement reuds the coumn vuues nto the vurubes decured n Step l. FETCH uses the loowng
syntux:
FETCH cursor_name
INTO variable[, variable ...];
where
cursor_name s the nume ol the cursor.
variable s the vurube nto whch u coumn vuue lrom the cursor s stored. You need
to provde mutchng vurubes lor euch coumn vuue.
1he loowng FETCH exumpe retreves u row lrom v_product_cursor und stores the
coumn vuues n the v_product_id, v_name, und v_price vurubes creuted eurer n Step l:
FETCH v_product_cursor
INTO v_product_id, v_name, v_price;
ecuuse u cursor muy contun muny rows, you need u oop to reud them. 1o lgure out when
to end the oop, you cun use the ooeun vurube v_product_cursor%NOTFOUND. 1hs
vurube s true when there ure no more rows to reud n v_product_cursor. 1he loowng
exumpe shows u oop:
LOOP
-- fetch the rows from the cursor
FETCH v_product_cursor
INTO v_product_id, v_name, v_price;
-- exit the loop when there are no more rows, as indicated by
-- the Boolean variable v_product_cursor%NOTFOUND (= true when
-- there are no more rows)
EXIT WHEN v_product_cursor%NOTFOUND;
-- use DBMS_OUTPUT.PUT_LINE() to display the variables
DBMS_OUTPUT.PUT_LINE(
'v_product_id = ' || v_product_id || ', v_name = ' || v_name ||
', v_price = ' || v_price
);
END LOOP;
Notce thut l've used DBMS_OUTPUT.PUT_LINE() to dspuy the v_product_id, v_name,
und v_price vurubes thut were reud lor euch row. ln u reu uppcuton, you mght use v_price
n u compex cucuuton.
Stcp S: CIusc thc Cursur
Step 5 s to cose the cursor usng the CLOSE stutement. Cosng u cursor lrees up system
resources. 1he loowng exumpe coses v_product_cursor:
CLOSE v_product_cursor;
348
Cruce Dutubuse ll SQL
1he loowng secton shows u compete scrpt thut contuns u lve steps.
CumpIctc fxampIc: pruduct_cursur.sqI
1he loowng product_cursor.sql scrpt s contuned n the SQL drectory:
-- product_cursor.sql displays the product_id, name,
-- and price columns from the products table using a cursor
SET SERVEROUTPUT ON
DECLARE
-- step 1: declare the variables
v_product_id products.product_id%TYPE;
v_name products.name%TYPE;
v_price products.price%TYPE;
-- step 2: declare the cursor
CURSOR v_product_cursor IS
SELECT product_id, name, price
FROM products
ORDER BY product_id;
BEGIN
-- step 3: open the cursor
OPEN v_product_cursor;
LOOP
-- step 4: fetch the rows from the cursor
FETCH v_product_cursor
INTO v_product_id, v_name, v_price;
-- exit the loop when there are no more rows, as indicated by
-- the Boolean variable v_product_cursor%NOTFOUND (= true when
-- there are no more rows)
EXIT WHEN v_product_cursor%NOTFOUND;
-- use DBMS_OUTPUT.PUT_LINE() to display the variables
DBMS_OUTPUT.PUT_LINE(
'v_product_id = ' || v_product_id || ', v_name = ' || v_name ||
', v_price = ' || v_price
);
END LOOP;
-- step 5: close the cursor
CLOSE v_product_cursor;
END;
/
1o run ths scrpt, loow these steps:
1. Connect to the dutubuse us store wth the pussword store_password.
2. Run the product_cursor.sql scrpt usng SQL*lus:
SQL> @ C:\SQL\product_cursor.sql
Chupter ll: lntroducng lL/SQL lrogrummng
349
NOTf
| ,our product_cursor.sql crp| n a d||crcn| drcc|or, |rom
C:\SQL, uc ,our ovn drcc|or, n |hc prcvou command.
1he output lrom product_cursor.sql s us loows:
v_product_id = 1, v_name = Modern Science, v_price = 19.95
v_product_id = 2, v_name = Chemistry, v_price = 30
v_product_id = 3, v_name = Supernova, v_price = 25.99
v_product_id = 4, v_name = Tank War, v_price = 13.95
v_product_id = 5, v_name = Z Files, v_price = 49.99
v_product_id = 6, v_name = 2412: The Return, v_price = 14.95
v_product_id = 7, v_name = Space Force 9, v_price = 13.49
v_product_id = 8, v_name = From Another Planet, v_price = 12.99
v_product_id = 9, v_name = Classical Music, v_price = 10.99
v_product_id = 10, v_name = Pop 3, v_price = 15.99
v_product_id = 11, v_name = Creative Yell, v_price = 14.99
v_product_id = 12, v_name = My Front Line, v_price = 13.49
Cursurs and fOR luups
You cun use u FOR oop to uccess the rows n u cursor. \hen you do ths, you don't huve to
expcty open und cose the cursorthe FOR oop does ths uutomutcuy lor you. 1he loowng
product_cursor2.sql scrpt uses u FOR oop to uccess the rows n v_product_cursor,
notce thut ths scrpt contuns ess code thun product_cursor.sql:
-- product_cursor2.sql displays the product_id, name,
-- and price columns from the products table using a cursor
-- and a FOR loop
SET SERVEROUTPUT ON
DECLARE
CURSOR v_product_cursor IS
SELECT product_id, name, price
FROM products
ORDER BY product_id;
BEGIN
FOR v_product IN v_product_cursor LOOP
DBMS_OUTPUT.PUT_LINE(
'product_id = ' || v_product.product_id ||
', name = ' || v_product.name ||
', price = ' || v_product.price
);
END LOOP;
END;
/
1o run the product_cursor2.sql scrpt, you ssue u commund smur to the loowng:
SQL> @ "C:\SQL\product_cursor2.sql"
3S0
Cruce Dutubuse ll SQL
1he output lrom ths scrpt s us loows:
product_id = 1, name = Modern Science, price = 19.95
product_id = 2, name = Chemistry, price = 30
product_id = 3, name = Supernova, price = 25.99
product_id = 4, name = Tank War, price = 13.95
product_id = 5, name = Z Files, price = 49.99
product_id = 6, name = 2412: The Return, price = 14.95
product_id = 7, name = Space Force 9, price = 13.49
product_id = 8, name = From Another Planet, price = 12.99
product_id = 9, name = Classical Music, price = 10.99
product_id = 10, name = Pop 3, price = 15.99
product_id = 11, name = Creative Yell, price = 14.99
product_id = 12, name = My Front Line, price = 13.49
OPfN-fOR Statcmcnt
You muy uso use the OPEN-FOR stutement wth u cursor, whch udds even more lexbty when
processng cursors becuuse you cun ussgn the cursor to u dllerent query. 1hs s shown n the
loowng product_cursor3.sql scrpt:
-- product_cursor3.sql displays the product_id, name,
-- and price columns from the products table using a cursor
-- variable and the OPEN-FOR statement
SET SERVEROUTPUT ON
DECLARE
-- declare a REF CURSOR type named t_product_cursor
TYPE t_product_cursor IS
REF CURSOR RETURN products%ROWTYPE;
-- declare a t_product_cursor object named v_product_cursor
v_product_cursor t_product_cursor;
-- declare an object to store columns from the products table
-- named v_product (of type products%ROWTYPE)
v_product products%ROWTYPE;
BEGIN
-- assign a query to v_product_cursor and open it using OPEN-FOR
OPEN v_product_cursor FOR
SELECT * FROM products WHERE product_id < 5;
-- use a loop to fetch the rows from v_product_cursor into v_product
LOOP
FETCH v_product_cursor INTO v_product;
EXIT WHEN v_product_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(
'product_id = ' || v_product.product_id ||
', name = ' || v_product.name ||
', price = ' || v_product.price
);
Chupter ll: lntroducng lL/SQL lrogrummng
END LOOP;
-- close v_product_cursor
CLOSE v_product_cursor;
END;
/
ln the DECLARE bock, the loowng stutement decures u REF CURSOR type numed
t_product_cursor (l uwuys put t_ ut the sturt ol type numes):
TYPE t_product_cursor IS
REF CURSOR RETURN products%ROWTYPE;
A REF CURSOR s u ponter to u cursor, und s smur to u ponter n the C++ progrummng
unguuge. 1he prevous stutement decures u user-delned type numed t_product_cursor,
und returns u row contunng the vurous coumns ol the products tube (ths s ndcuted usng
%ROWTYPE). 1hs user-delned type muy be used to decure un uctuu obect, us shown n the
loowng stutement, whch decures un obect numed v_product_cursor:
v_product_cursor t_product_cursor;
1he loowng stutement decures un obect to store coumns lrom the products tube numed
v_product (ol type products%ROWTYPE):
v_product products%ROWTYPE;
ln the BEGIN bock, v_product_cursor s ussgned u query und opened by the loowng
OPEN-FOR stutement:
OPEN v_product_cursor FOR
SELECT * FROM products WHERE product_id < 5;
Alter ths stutement s executed, v_product_cursor w be ouded wth the lrst lour rows
n the products tube. 1he query ussgned to v_product_cursor cun be uny vud SELECT
stutement, ths meuns you cun re-use the cursor und ussgn unother query to the cursor uter n the
lL/SQL code.
Next, the loowng oop letches the rows lrom v_product_cursor nto v_product und
dspuys the row detus:
LOOP
FETCH v_product_cursor INTO v_product;
EXIT WHEN v_product_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(
'product_id = ' || v_product.product_id ||
', name = ' || v_product.name ||
', price = ' || v_product.price
);
END LOOP;
Alter the oop, v_product_cursor s cosed usng the loowng stutement:
CLOSE v_product_cursor;
1he output lrom ths scrpt s the sume us the output lrom product_cursor2.sql.
3S2
Cruce Dutubuse ll SQL
Uncunstraincd Cursurs
1he cursors n the prevous secton u huve u speclc return type, these cursors ure known us
construned cursors. 1he return type lor u construned cursor must mutch the coumns n the
query thut s run by the cursor. An unconstruned cursor hus no return type, und cun therelore
run uny query.
1he use ol un unconstruned cursor s shown n the loowng unconstrained_cursor
.sql scrpt, notce v_cursor n the code s used to run two dllerent queres:
-- This script shows the use of unconstrained cursors
SET SERVEROUTPUT ON
DECLARE
-- declare a REF CURSOR type named t_cursor (this has no return
-- type and can therefore run any query)
TYPE t_cursor IS REF CURSOR;
-- declare a t_cursor object named v_cursor
v_cursor t_cursor;
-- declare an object to store columns from the products table
-- named v_product (of type products%ROWTYPE)
v_product products%ROWTYPE;
-- declare an object to store columns from the customers table
-- named v_customer (of type customers%ROWTYPE)
v_customer customers%ROWTYPE;
BEGIN
-- assign a query to v_cursor and open it using OPEN-FOR
OPEN v_cursor FOR
SELECT * FROM products WHERE product_id < 5;
-- use a loop to fetch the rows from v_cursor into v_product
LOOP
FETCH v_cursor INTO v_product;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(
'product_id = ' || v_product.product_id ||
', name = ' || v_product.name ||
', price = ' || v_product.price
);
END LOOP;
-- assign a new query to v_cursor and open it using OPEN-FOR
OPEN v_cursor FOR
SELECT * FROM customers WHERE customer_id < 3;
-- use a loop to fetch the rows from v_cursor into v_product
LOOP
FETCH v_cursor INTO v_customer;
EXIT WHEN v_cursor%NOTFOUND;
Chupter ll: lntroducng lL/SQL lrogrummng
3S3
DBMS_OUTPUT.PUT_LINE(
'customer_id = ' || v_customer.customer_id ||
', first_name = ' || v_customer.first_name ||
', last_name = ' || v_customer.last_name
);
END LOOP;
-- close v_cursor
CLOSE v_cursor;
END;
/
1o run the unconstrained_cursor.sql scrpt, you ssue u commund smur to the
loowng:
SQL> @ "C:\SQL\unconstrained_cursor.sql"
1he output lrom ths scrpt s us loows:
product_id = 1, name = Modern Science, price = 19.95
product_id = 2, name = Chemistry, price = 30
product_id = 3, name = Supernova, price = 25.99
product_id = 4, name = Tank War, price = 13.95
customer_id = 1, first_name = John, last_name = Brown
customer_id = 2, first_name = Cynthia, last_name = Green
You' eurn more ubout REF CURSOR vurubes uter n ths chupter und more ubout user-
delned types n the next chupter.
fxccptiuns
Lxceptons ure used to hunde run-tme errors n your lL/SQL code. Lurer, you suw the loowng
lL/SQL exumpe thut contuns un EXCEPTION bock:
DECLARE
v_width INTEGER;
v_height INTEGER := 2;
v_area INTEGER := 6;
BEGIN
-- set the width equal to the area divided by the height
v_width := v_area / v_height;
DBMS_OUTPUT.PUT_LINE('v_width = ' || v_width);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/
1he EXCEPTION bock n ths exumpe hundes un uttempt to dvde u number by zero. ln lL/
SQL termnoogy, the EXCEPTION bock ca|chc u ZERO_DIVIDE excepton thut s racd n the
BEGIN bock (uthough n the exumpe code, ZERO_DIVIDE s never uctuuy rused). 1he ZERO_
DIVIDE excepton und the other common exceptons ure shown n 1ube ll-l.
3S4
Cruce Dutubuse llg SQL
fxccptiun frrur Dcscriptiun
ACCESS_INTO_NULL ORA-06530
An uttempt wus mude to ussgn vuues to the
uttrbutes ol un unntuzed obect. (You' eurn
ubout obects n Chupter l2.)
CASE_NOT_FOUND ORA-06592
None ol the WHEN cuuses ol u CASE stutement
wus seected, und there s no deluut ELSE cuuse.
COLLECTION_IS_NULL ORA-06531
An uttempt wus mude to cu u coecton method
(other thun EXISTS) on un unntuzed nested
tube or vurruy, or un uttempt wus mude to ussgn
vuues to the eements ol un unntuzed nested
tube or vurruy. (You' eurn ubout coectons n
Chupter l3.)
CURSOR_ALREADY_OPEN ORA-06511
An uttempt wus mude to open un ureudy open
cursor. 1he cursor must be cosed belore t cun
be reopened.
DUP_VAL_ON_INDEX ORA-00001
An uttempt wus mude to store dupcute vuues n
u coumn thut s construned by u unque ndex.
INVALID_CURSOR ORA-01001
An uttempt wus mude to perlorm un egu cursor
operuton, such us cosng un unopened cursor.
INVALID_NUMBER ORA-01722
An uttempt to convert u churucter strng nto
u number lued becuuse the strng does not
represent u vud number. Note: ln lL/SQL
stutements, VALUE_ERROR s rused nsteud ol
INVALID_NUMBER.
LOGIN_DENIED ORA-01017
An uttempt wus mude to connect to u dutubuse
usng un nvud user nume or pussword.
NO_DATA_FOUND ORA-01403
A SELECT INTO stutement returned no rows,
or un uttempt wus mude to uccess u deeted
eement n u nested tube or un unntuzed
eement n un ndex by tube.
NOT_LOGGED_ON ORA-01012
An uttempt wus mude to uccess u dutubuse tem
wthout beng connected to the dutubuse.
PROGRAM_ERROR ORA-06501
lL/SQL hud un nternu probem.
ROWTYPE_MISMATCH ORA-06504
1he host cursor vurube und the lL/SQL cursor
vurube nvoved n un ussgnment huve
ncomputbe return types. lor exumpe, when
un open host cursor vurube s pussed to u stored
procedure or luncton, the return types ol the
uctuu und lormu purumeters must be computbe.
TABlf 11-1 rcdc|ncd xccp|on
Chupter ll: lntroducng lL/SQL lrogrummng
3SS
1he loowng sectons show exumpes thut ruse some ol the exceptons shown n 1ube ll-l.
ZfRO_DlVlDf fxccptiun
1he ZERO_DIVIDE excepton s rused when un uttempt s mude to dvde u number by zero. 1he
loowng exumpe uttempts to dvde l by 0 n the BEGIN bock und therelore ruses the ZERO_
DIVIDE excepton:
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN ZERO_DIVIDE THEN
fxccptiun frrur Dcscriptiun
SELF_IS_NULL ORA-30625
An uttempt wus mude to cu u MEMBER method
on u nu obect. 1hut s, the but-n purumeter
SELF (whch s uwuys the lrst purumeter pussed
to u MEMBER method) s nu.
STORAGE_ERROR ORA-06500
1he lL/SQL modue run out ol memory or the
memory hus been corrupted.
SUBSCRIPT_BEYOND_COUNT ORA-06533
An uttempt wus mude to relerence u nested tube
or vurruy eement usng un ndex number urger
thun the number ol eements n the coecton.
SUBSCRIPT_OUTSIDE_LIMIT ORA-06532
An uttempt wus mude to relerence u nested tube
or vurruy eement usng un ndex number thut s
outsde the egu runge (l lor exumpe).
SYS_INVALID_ROWID ORA-01410
1he converson ol u churucter strng to u
unversu rowd lued becuuse the churucter
strng does not represent u vud rowd.
TIMEOUT_ON_RESOURCE ORA-00051
A tmeout occurred whe the dutubuse wus
wutng lor u resource.
TOO_MANY_ROWS ORA-01422
A SELECT INTO stutement returned more thun
one row.
VALUE_ERROR ORA-06502
An urthmetc, converson, truncuton, or sze-
construnt error occurred. lor exumpe, when
seectng u coumn vuue nto u churucter vurube,
l the vuue s onger thun the decured ength ol
the vurube, lL/SQL uborts the ussgnment und
ruses VALUE_ERROR.
Note: ln lL/SQL stutements, VALUE_ERROR s
rused l the converson ol u churucter strng nto
u number lus. ln SQL stutements, INVALID_
NUMBER s rused nsteud ol VALUE_ERROR.
ZERO_DIVIDE ORA-01476
An uttempt wus mude to dvde u number by zero.
TABlf 11-1 rcdc|ncd xccp|on (contnued)
3S6
Cruce Dutubuse ll SQL
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/
Division by zero
\hen un excepton s rused, progrum contro pusses to the EXCEPTION bock und the WHEN
cuuse s exumned lor u mutchng excepton, the code nsde the mutchng cuuse s then executed.
ln the prevous exumpe, the ZERO_DIVIDE excepton s rused n the BEGIN bock, und progrum
contro then pusses to the EXCEPTION bock, u mutchng excepton s lound n the WHEN cuuse,
und the code nsde the cuuse s executed.
ll no mutchng excepton s lound, the excepton s propuguted to the encosng bock. lor
exumpe, l the EXCEPTION bock wus omtted lrom the prevous code, the excepton s propuguted
up to SQL*lus:
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
END;
BEGIN
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at line 2
As you cun see, SQL*lus dspuys u deluut error thut shows the ne numbers, the Cruce
error codes, und u smpe descrpton.
DUP_VAl_ON_lNDfX fxccptiun
1he DUP_VAL_ON_INDEX excepton s rused when un uttempt s mude to store dupcute vuues
n u coumn thut s construned by u unque ndex. 1he loowng exumpe uttempts to nsert u row
n the customers tube wth u customer_id ol l, ths cuuses DUP_VAL_ON_INDEX to be
rused, becuuse the customers tube ureudy contuns u row wth u customer_id ol l:
BEGIN
INSERT INTO customers (
customer_id, first_name, last_name
) VALUES (
1, 'Greg', 'Green'
);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('Duplicate value on an index');
END;
/
Duplicate value on an index
lNVAllD_NUMBfR fxccptiun
1he INVALID_NUMBER excepton s rused when un uttempt s mude to convert un nvud
churucter strng nto u number. 1he loowng exumpe uttempts to convert the strng 123X to
Chupter ll: lntroducng lL/SQL lrogrummng
3S7
u number thut s used n un INSERT, whch cuuses INVALID_NUMBER to
be rused becuuse 123X s not u vud number:
BEGIN
INSERT INTO customers (
customer_id, first_name, last_name
) VALUES (
'123X', 'Greg', 'Green'
);
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_OUTPUT.PUT_LINE('Conversion of string to number failed');
END;
/
Conversion of string to number failed
OTHfRS fxccptiun
You cun use the OTHERS excepton to hunde u exceptons, us shown here:
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An exception occurred');
END;
/
An exception occurred
ecuuse OTHERS mutches u exceptons, you must st t ulter uny speclc exceptons n your
EXCEPTION bock. ll you uttempt to st OTHERS esewhere, the dutubuse returns the error PLS-
00370, lor exumpe:
SQL> BEGIN
2 DBMS_OUTPUT.PUT_LINE(1 / 0);
3 EXCEPTION
4 WHEN OTHERS THEN
5 DBMS_OUTPUT.PUT_LINE('An exception occurred');
6 WHEN ZERO_DIVIDE THEN
7 DBMS_OUTPUT.PUT_LINE('Division by zero');
8 END;
9 /
WHEN OTHERS THEN
*
ERROR at line 4:
ORA-06550: line 4, column 3:
PLS-00370: OTHERS handler must be last among the exception
handlers of a block
ORA-06550: line 0, column 0:
PL/SQL: Compilation unit analysis terminated
3S8
Cruce Dutubuse ll SQL
Pruccdurcs
A procedure contuns u group ol SQL und lL/SQL stutements. lrocedures uow you to centruze
your busness ogc n the dutubuse und muy be used by uny progrum thut uccesses the dutubuse.
ln ths secton, you' eurn how to
Creute u procedure.
Cu u procedure.
Cet nlormuton on procedures.
Drop u procedure.
Vew errors n u procedure.
Crcating a Pruccdurc
You creute u procedure usng the CREATE PROCEDURE stutement. 1he smpled syntux lor the
CREATE PROCEDURE stutement s us loows:
CREATE [OR REPLACE] PROCEDURE procedure_name
[(parameter_name [IN | OUT | IN OUT] type [, ...])]
{IS | AS}
BEGIN
procedure_body
END procedure_name;
where
OR REPLACE meuns the procedure s to repuce un exstng procedure.
procedure_name s the nume ol the procedure.
parameter_name s the nume ol u purumeter thut s pussed to the procedure. You muy
puss mutpe purumeters to u procedure.
IN | OUT | IN OUT s the ol the purumeter. You muy pck one ol the loowng
modes lor euch purumeter:
IN, whch s the deluut mode lor u purumeter. An IN purumeter must be set to u
vuue when the procedure s run. 1he vuue ol un IN purumeter cunnot be chunged
n the procedure body.
OUT, whch meuns the purumeter s set to u vuue n the procedure body.
IN OUT, whch meuns the purumeter cun huve u vuue when the procedure s run,
und the vuue cun be chunged n the body.
type s the type ol the purumeter.
procedure_body contuns the uctuu code lor the procedure.
1he loowng exumpe creutes u procedure numed update_product_price()ths
procedure, und the other lL/SQL code shown n the rest ol ths chupter, wus creuted when you
Chupter ll: lntroducng lL/SQL lrogrummng