Tải bản đầy đủ (.pdf) (103 trang)

Chapter 16: Advanced Use of Functions and ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.69 MB, 103 trang )

LPAD('o',ROUND(AVG(ReturnedDate-CheckOutDate)/2,0),'o')
AS Graph
from BOOKSHELF_CHECKOUT
group by Name
order by AVG(ReturnedDate-CheckOutDate);
Day
1 2 3 4 5 6 7
NAME DAYSOUT 0 0 0 0 0 0 0

DORAH TALBOT 13 ooooooo
EMILY TALBOT 14 ooooooo
JED HOPKINS 18 ooooooooo
GERHARDT KENTGEN 19 ooooooooo
PAT LAVAY 21 oooooooooo
FRED FULLER 24 oooooooooooo
ROLAND BRANDT 52 oooooooooooooooooooooooooo
7 rows selected.
This use of LPAD is similar to what was done in Chapter 13, where the cows and bulls
were shown in their family tree. Basically, a lowercase
o
is the column here, and it is padded
on the left with a number of additional lowercase
o
’s, to the maximum width determined by
ROUND(AVG(ReturnedDate-CheckOutDate)/2,0).
Notice that the scale of the column heading is in increments of two. A simple change to the
SQL will produce a classic graph rather than a bar chart. The literal column is changed from an
o
to a lowercase
x
, and the padding on the left is done by spaces.


select Name,
AVG(ReturnedDate-CheckOutDate) DaysOut,
LPAD('x',ROUND(AVG(ReturnedDate-CheckOutDate)/2,0),' ')
AS Graph
from BOOKSHELF_CHECKOUT
group by Name
order by AVG(ReturnedDate-CheckOutDate);
Day
1 2 3 4 5 6 7
NAME DAYSOUT 0 0 0 0 0 0 0

DORAH TALBOT 13 x
EMILY TALBOT 14 x
JED HOPKINS 18 x
GERHARDT KENTGEN 19 x
PAT LAVAY 21 x
FRED FULLER 24 x
ROLAND BRANDT 52 x
7 rows selected.
Chapter 16: Advanced Use of Functions and Variables
299
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:299
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:21 PM
Color profile: Generic CMYK printer profile
Composite Default screen
300

Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:300
Another way to graph the data is by its distribution rather than by person. First, a view is
created that puts each number of days out into its decile. Thus 13, 14, 18, and 19 become 10;
20 through 29 become 20; 30 through 39 become 30; and so on:
create or replace view DaysOutRange as
select ROUND((ReturnedDate-CheckOutDate),-1) Decile,
COUNT(*) Counter
from BOOKSHELF_CHECKOUT
group by ROUND((ReturnedDate-CheckOutDate),-1);
Next, a column heading is set up, similar to the previous heading although shorter and in
increments of one:
column Graph Heading 'Count| 1 1| 5 0 5'-
justify c
column Graph format a15
The next SQL determines the count of values that are represented in each decile.
select Decile, Counter,
LPAD('o',Counter,'o') AS Graph
from DAYSOUTRANGE;
Count
1 1
DECILE COUNTER 5 0 5

10 8 oooooooo
20 5 ooooo
30 3 ooo
50 2 oo

60 1 o
If one of the dates were NULL, the group by output would include a NULL group, with a
count and display similar to those shown in this listing. If you want to customize how Oracle
handles books with NULL ReturnedDate values, you can use the NVL function to replace the
NULL value with one of your choosing (such as SysDate). For more complex data replacement
logic, you can use DECODE and CASE, as introduced later in this chapter and more fully
described in Chapter 17.
Using TRANSLATE
TRANSLATE converts characters in a string into different characters, based on a substitution plan
you give it, from
if
to
then
:
TRANSLATE(
string
,
if
,
then
)
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:22 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 16: Advanced Use of Functions and Variables
301
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16

Blind Folio 16:301
In the following SQL, the letters in a sentence are replaced. Any time an uppercase
T
is
detected, it is replaced by an uppercase
T
. In effect, nothing changes. Any time an uppercase
vowel is detected, however, it is replaced by a lowercase version of the same vowel. Any
letter not in the TAEIOU string is left alone. When a letter is found in TAEIOU, its position is
checked in the TAEIOU string, and the letter there is substituted. Thus, the letter
E
, at position
3 in TAEIOU, is replaced by
e
, in position 3 of Taeiou:
select TRANSLATE('NOW VOWELS ARE UNDER ATTACK','TAEIOU','Taeiou')
from DUAL;
TRANSLATE('NOWVOWELSAREUNDE

NoW VoWeLS aRe uNDeR aTTaCK
Eliminating Characters
Extending this logic, what happens if the
if
string is TAEIOU and the
then
string is only
T
?
Checking for the letter
E

(as in the word VOWELS) finds it in position 3 of TAEIOU. There is
no position 3 in the
then
string (which is just the letter
T
), so the value in position 3 is nothing.
So
E
is replaced by nothing. This same process is applied to all of the vowels. They appear in
the
if
string, but not in the
then
string. As a result, they disappear, as shown here:
select TRANSLATE('NOW VOWELS ARE UNDER ATTACK','TAEIOU','T')
from DUAL;
TRANSLATE('NOWVOWE

NW VWLS R NDR TTCK
This feature of TRANSLATE, the ability to eliminate characters from a string, can prove very
useful in cleaning up data. Recall the magazine titles in Chapter 7:
select Title from MAGAZINE;
TITLE

THE BARBERS WHO SHAVE THEMSELVES.
"HUNTING THOREAU IN NEW HAMPSHIRE"
THE ETHNIC NEIGHBORHOOD
RELATIONAL DESIGN AND ENTHALPY
"INTERCONTINENTAL RELATIONS."
The method used in Chapter 7 to clean out the periods and double quotes was a nested

combination of LTRIM and RTRIM:
select LTRIM( RTRIM(Title,'."') ,'"') from MAGAZINE;
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:22 PM
Color profile: Generic CMYK printer profile
Composite Default screen
The same goal can be accomplished with a single use of TRANSLATE:
select TRANSLATE(Title,'T".','T') AS TITLE
from MAGAZINE;
TITLE

THE BARBERS WHO SHAVE THEMSELVES
HUNTING THOREAU IN NEW HAMPSHIRE
THE ETHNIC NEIGHBORHOOD
RELATIONAL DESIGN AND ENTHALPY
INTERCONTINENTAL RELATIONS
In the listing, you have to include one character in the
then
string—in this case, the letter
T
translates to the letter
T
. All other characters in the
if
set are eliminated.
Complex Cut and Paste
The NAME table contains a list of names as you might receive them from a mailing list company
or another application. First name, last name, and initials are all in one column:
column Name format a25
select Name from NAME;

NAME

HORATIO NELSON
VALDO
MARIE DE MEDICIS
FLAVIUS JOSEPHUS
EDYTHE P. M. GAMMIERE
Suppose you want to cut and paste these names and put them into a table with FirstName
and LastName columns. How would you go about it? The technique you learned in Chapter 7
involved using INSTR to locate a space, and using the number it returns in a SUBSTR to clip
out the portion up to that space. Here’s an attempt to do just that for the first name:
select SUBSTR(Name,1,INSTR(Name,' '))
from NAME;
SUBSTR(NAME,1,INSTR(NAME,

HORATIO
MARIE
FLAVIUS
EDYTHE
302
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:302
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:22 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 16: Advanced Use of Functions and Variables

303
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:303
VALDO has vanished! The problem is that these names are not as consistent as the authors’
names were in Chapter 7. One of these names (probably a magician) is only one name long, so
there is no space for the INSTR to find. When INSTR has an unsuccessful search, it returns a 0.
The SUBSTR of the name VALDO, starting at position 1 and going for 0 positions, is nothing, so
he disappears. This is solved with DECODE. In place of this:
INSTR(Name,' ')
you put the entire expression, like this:
DECODE(INSTR(Name,' '),0,99,INSTR(Name,' '))
DECODE’s format is this:
DECODE(
value
,
if1
,
then1
[,
if2
,
then2
,
if3
,
then3
]


,
else
)
In the preceding example, DECODE tests the value of INSTR(Name, ‘ ‘). If it is equal to 0,
DECODE substitutes 99; otherwise, it returns the default value, which is also INSTR(Name, ‘ ‘).
The choice of 99 as a substitute is arbitrary. It will create an effective SUBSTR function for
VALDO that looks like this:
SUBSTR('VALDO',1,99)
This works because SUBSTR will clip out text from the starting number, 1, to the ending
number or the end of the string, whichever comes first. With the DECODE function in place,
the first names are retrieved correctly:
select SUBSTR(Name,1,
DECODE(INSTR(Name,' '),0,99,INSTR(Name,' ')))
from NAME;
SUBSTR(NAME,1,DECODE(INST

HORATIO
VALDO
MARIE
FLAVIUS
EDYTHE
How about the last names? You could use INSTR again to search for a space, and use the
location of the space in the string, plus one (+1), as the starting point for SUBSTR. No ending
point is required for SUBSTR, because you want it to go to the end of the name. This is what
happens:
select SUBSTR(Name,INSTR(Name,' ')+1)
from NAME;
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:23 PM
Color profile: Generic CMYK printer profile

Composite Default screen
304
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:304
SUBSTR(NAME,INSTR(NAME,''

NELSON
VALDO
DE MEDICIS
JOSEPHUS
P. M. GAMMIERE
This didn’t quite work. One solution is to use three INSTR functions, looking successively
for the first, second, or third occurrence of a space in the name. Each of these INSTRs will return
either the location where it found a space or a 0 if it didn’t find any. In a name with only one
space, the second and third INSTRs will both return 0. The GREATEST function, therefore, will
pick the number returned by the INSTR that found the space furthest into the name:
select SUBSTR(Name,
GREATEST(INSTR(Name,' '),INSTR(Name,' ',1,2),
INSTR(Name,' ',1,3)) +1)
from NAME;
SUBSTR(NAME,GREATEST(INST

NELSON
VALDO
MEDICIS
JOSEPHUS
GAMMIERE

Except for the fact that you also got VALDO again, this worked. (GREATEST also could have
been used similarly in place of DECODE in the previous example.) There is a second and simpler
method:
select SUBSTR(Name,INSTR(Name,' ',-1)+1)
from NAME;
SUBSTR(NAME,INSTR(NAME,''

NELSON
VALDO
MEDICIS
JOSEPHUS
GAMMIERE
The -1 in the INSTR tells it to start its search in the final position and go
backward,
or right
to left, in the Name column. When it finds the space, INSTR returns its position, counting from
the left as usual. The -1 simply makes INSTR start searching from the end rather than from the
beginning. A -2 would make it start searching from the second position from the end, and so on.
The +1 in the SUBSTR command has the same purpose as in the previous example: Once the
space is found, SUBSTR has to move one position to the right to begin clipping out the Name. If
no space is found, the INSTR returns 0, and SUBSTR therefore starts with position 1. That’s why
VALDO made the list.
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:23 PM
Color profile: Generic CMYK printer profile
Composite Default screen
How do you get rid of VALDO? Add an ending position to the SUBSTR instead of its default
(which goes automatically all the way to the end). The ending position is found by using this:
DECODE(INSTR(Name,' '),0,0,LENGTH(Name))
which says, “Find the position of a space in the Name. If the position is 0, return 0; otherwise,

return the LENGTH of the Name.” For VALDO, the DECODE produces 0 as the ending position
for SUBSTR, so nothing is displayed. For any other name, because there is a space somewhere,
the LENGTH of the Name becomes the ending position for the SUBSTR, so the whole last name
is displayed.
This is similar to the DECODE used to extract the first name, except that the value 99 used
there has been replaced by LENGTH(Name), which will always work, whereas 99 would fail
for a name longer than 99 characters. This won’t matter here, but in other uses of DECODE and
SUBSTR, it could be important:
select SUBSTR(Name,
INSTR(Name,' ',-1)+1,
DECODE(INSTR(Name,' '), 0, 0, LENGTH(Name)))
from NAME;
SUBSTR(NAME,INSTR(NAME,''

NELSON
MEDICIS
JOSEPHUS
GAMMIERE
This DECODE also could have been replaced by a GREATEST:
select SUBSTR(Name,
INSTR(Name,' ',-1)+1,
GREATEST(INSTR(Name,' '),0))
from NAME;
A third method to accomplish the same end uses RTRIM. Remember that RTRIM eliminates
everything specified in its
set
from the right side of a string until it encounters any character not
in its
set
. The RTRIM here effectively erases all the letters on the right until it hits the first space

(just before the last name) or reaches the beginning of the string:
select
SUBSTR(Name,LENGTH(RTRIM(NAME,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
+1)
from NAME;
SUBSTR(NAME,LENGTH(RTRIM(

NELSON
Chapter 16: Advanced Use of Functions and Variables
305
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:305
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:24 PM
Color profile: Generic CMYK printer profile
Composite Default screen
MEDICIS
JOSEPHUS
GAMMIERE
LENGTH then measures the resulting string (with the last name erased). This tells you the
position of the space before the last name. Add 1 to this number, do a SUBSTR starting there,
and you’ll get just the last name. Let’s create a table with FirstName and LastName columns
(you’ll see complete details on creating tables in Chapter 18):
create table TWONAME(
FirstName VARCHAR2(25),
LastName VARCHAR2(25)
);
Table created.

Now, use an insert with a subquery to load the table data with the first and last names from
the NAME table:
insert into TWONAME (FirstName, LastName)
select
SUBSTR(Name,1,DECODE(INSTR(Name,' '),0,99,INSTR(Name,' '))),
SUBSTR(Name,LENGTH(RTRIM(NAME,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'))+1)
from NAME;
Check the contents of the TWONAME table:
select * from TWONAME;
FIRSTNAME LASTNAME

HORATIO NELSON
VALDO
MARIE MEDICIS
FLAVIUS JOSEPHUS
EDYTHE GAMMIERE
You can use similar techniques to extract the middle initial or initials, and apply these methods
elsewhere as well, such as to addresses, product descriptions, company names, and so on.
When moving data from an old system to a new one, reformatting is frequently necessary
and often difficult. The facilities exist in SQL, but they require some knowledge of how the
functions work and some thoughtfulness to avoid the kinds of difficulties shown in the examples
so far in this chapter.
306
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:306
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:24 PM

Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 16: Advanced Use of Functions and Variables
307
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:307
Counting String Occurrences
Within Larger Strings
You can use a combination of the LENGTH and REPLACE functions to determine how many
times a string (such as ABC) occurs within a larger string (such as ABCDEFABC). The REPLACE
function replaces a character or characters in a string with zero or more characters. Thus,
REPLACE('ADAH', 'A', 'BLAH')
will evaluate the string ADAH. Everywhere an
A
is found, it will be replaced with the string
BLAH. Thus, the function shown in this example will return the string BLAHDBLAHH.
You can use the REPLACE function to eliminate characters from strings. For example, you
can replace the character string you’re searching for with NULL values. Thus,
REPLACE('GEORGE', 'GE', NULL)
returns a value of OR. The two separate occurrences of GE in GEORGE were each set to NULL.
You can use the REPLACE function to determine how many times a string (like GE) is found
in a larger string (like GEORGE). First, replace the string with NULL values:
select REPLACE('GEORGE', 'GE', NULL)
from DUAL;
The result of that query will be OR:
RE

OR

More importantly, the LENGTH of the result of that query will provide information about
how many times the string was found. The following query will tell you how long the resulting
string is:
select LENGTH(REPLACE('GEORGE', 'GE', NULL))
from DUAL;
LENGTH(REPLACE('GEORGE','GE',NULL))

2
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:24 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Now you can tell how many times the string was found. If you subtract the length of the
shortened string from the original string and divide that difference by the length of the search
string, the result will be the number of times the search string was found:
select (LENGTH('GEORGE')
- LENGTH(REPLACE('GEORGE', 'GE', NULL)) )
/ LENGTH('GE') AS Counter
from DUAL;
COUNTER

2
This example uses strings instead of character columns in order to be simpler to understand;
in real applications, you would replace the original string (GEORGE) with your column name.
The length of the string GEORGE is six characters. The length of GEORGE after GE is replaced
with NULL is two characters. Therefore, the difference in the lengths of the original and
shortened strings is four characters. Dividing four characters by the length of the search string
(two characters) tells you that the string was found twice.
The only problem with this method occurs when the search string is zero characters in length
(since you cannot divide by zero). Searching for a zero-length string may indicate a problem with

the application logic that initiated the search.
Additional Facts About Variables
The command accept forces SQLPLUS to define the variable as equal to the entered value,
and it can do this with a text message, with control over the datatype entered, and even with
the response blanked out from viewing (such as for passwords; see the entry for accept in the
Alphabetical Reference).
You can pass arguments to a start file when it is started by embedding numbered variables
in the select statements (rather than variables with names).
To select all of the checkout records between one date and another, the select statement
might look like this:
select * from BOOKSHELF_CHECKOUT
where CheckOutDate BETWEEN '&1' AND '&2';
That query can be saved as a file (such as checkout.sql) and started from within SQLPLUS:
start checkout.sql 01-JAN-02 31-JAN-02
The start file checkout.sql would begin, with 01-JAN-02 substituted for &1, and 31-JAN-02
substituted for &2. As with other variables, character and DATE datatypes must be enclosed
in single quotation marks in the SQL statement. One limitation of this is that each argument
following the word start must be a single word without spaces.
Variable substitutions are not restricted to the SQL statement. The start file also may use
variables for such things as SQLPLUS commands.
308
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:308
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:25 PM
Color profile: Generic CMYK printer profile
Composite Default screen

Variables can be concatenated simply by pushing them together. You can concatenate a
variable with a constant by using a period:
select * from BOOKSHELF_CHECKOUT
where CheckOutDate BETWEEN '01-&StartMonth 02' AND '01-&EndMonth 02';
This select statement will query for a starting month and an ending month, then build the two
dates using a period as a concatenation operator.
NOTE
No period is necessary before the variable—only after it. The
ampersand (&) tells SQLPLUS that a variable is beginning.
Related set Commands
Normally, the ampersand denotes a variable. This can be changed with set define followed by
the single symbol that you’d prefer to use to denote a variable.
set escape defines a character you can place just in front of the ampersand (or other defined
symbol) so that SQLPLUS will treat the symbol as a literal, not as denoting a variable.
set concat can change the default concatenation operator for variables, which is the period,
to another single symbol. This variable concatenation is used in addition to, and separately from,
the SQL concatenation operator, which is usually two vertical bars (||).
set scan turns variable substitution off or on for the SQL statement. If scan is off, any variable
in a select statement is treated as a literal—for example, &Test is treated as the literal word
&Test, not as the value it is defined as. Variables used in SQLPLUS commands, however, are
still substituted as before.
As of Oracle9i, set scan on/off is deprecated—use set define in place of set scan.
See the set command in the Alphabetical Reference for additional environment
configuration options.
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:309
Chapter 16: Advanced Use of Functions and Variables
309

P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:25 PM
Color profile: Generic CMYK printer profile
Composite Default screen
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 16
Blind Folio 16:310
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:25 PM
Color profile: Generic CMYK printer profile
Composite Default screen
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:311
CHAPTER
17
DECODE and CASE:
if, then, and else in SQL
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:26 PM
Color profile: Generic CMYK printer profile
Composite Default screen
T
he DECODE function is without doubt one of the most powerful in Oracle’s SQL.
It is one of several extensions Oracle added to the standard SQL language. This
chapter will explore a number of ways that DECODE can be used, including the
generation of “cross-tab” reports. As of Oracle9i, you can also use the CASE
function and the COALESCE function to execute complex logical tests within

your SQL statements.
This is an advanced and often difficult chapter. Most of what is illustrated here is unnecessary
for the vast majority of reporting; don’t be concerned if it seems beyond your needs. Its real value
is more for sophisticated reporting and programming.
if, then, else
In programming and logic, a common construction of a problem is in the pattern
if, then, else.
For example,
if
this day is a Saturday,
then
Adah will play at home;
if
this day is a Sunday,
then
Adah will go to her grandparent’s home;
if
this day is a holiday,
then
Adah will go over to Aunt
Dora’s;
else
Adah will go to school. In each case, “this day” was tested, and if it was one of a
list of certain days, then a certain result followed, or else (if it was none of those days) another
result followed. DECODE follows this kind of logic. Chapter 10 provided an introduction that
demonstrated the basic structure and usage of DECODE.
This is DECODE’s format:
DECODE(
value
,

if1
,
then1
,
if2
,
then2
,
if3
,
then3
,

,
else
)
value
represents any column in a table (regardless of datatype) or any result of a computation,
such as one date minus another, a SUBSTR of a character column, one number times another,
and so on.
value
is tested for each row. If
value
equals
if1
, then the result of the DECODE is
then1
; if
value
equals

if2
, then the result of the DECODE is
then2
; and so on, for virtually as
many
if/then
pairs as you can construct. If
value
equals none of the
if
s, then the result of the
DECODE is
else
. Each of the
if
s,
then
s, and the
else
also can be a column or the result of a
function or computation. You can have up to 255 elements within the parentheses.
Let’s consider the checkout history for the bookshelf, as recorded in the BOOKSHELF_
CHECKOUT table:
column Name format a16
column Title format a24 word_wrapped
set pagesize 60
select * from BOOKSHELF_CHECKOUT;
NAME TITLE CHECKOUTD RETURNEDD

JED HOPKINS INNUMERACY 01-JAN-02 22-JAN-02

GERHARDT KENTGEN WONDERFUL LIFE 02-JAN-02 02-FEB-02
DORAH TALBOT EITHER/OR 02-JAN-02 10-JAN-02
EMILY TALBOT ANNE OF GREEN GABLES 02-JAN-02 20-JAN-02
PAT LAVAY THE SHIPPING NEWS 02-JAN-02 12-JAN-02
ROLAND BRANDT THE SHIPPING NEWS 12-JAN-02 12-MAR-02
312
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:312
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:26 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
313
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:313
ROLAND BRANDT THE DISCOVERERS 12-JAN-02 01-MAR-02
ROLAND BRANDT WEST WITH THE NIGHT 12-JAN-02 01-MAR-02
EMILY TALBOT MIDNIGHT MAGIC 20-JAN-02 03-FEB-02
EMILY TALBOT HARRY POTTER AND THE 03-FEB-02 14-FEB-02
GOBLET OF FIRE
PAT LAVAY THE MISMEASURE OF MAN 12-JAN-02 12-FEB-02
DORAH TALBOT POLAR EXPRESS 01-FEB-02 15-FEB-02
DORAH TALBOT GOOD DOG, CARL 01-FEB-02 15-FEB-02
GERHARDT KENTGEN THE MISMEASURE OF MAN 13-FEB-02 05-MAR-02

FRED FULLER JOHN ADAMS 01-FEB-02 01-MAR-02
FRED FULLER TRUMAN 01-MAR-02 20-MAR-02
JED HOPKINS TO KILL A MOCKINGBIRD 15-FEB-02 01-MAR-02
DORAH TALBOT MY LEDGER 15-FEB-02 03-MAR-02
GERHARDT KENTGEN MIDNIGHT MAGIC 05-FEB-02 10-FEB-02
As you look through the checkout list, you realize that some of the books were checked out
for a rather long time. You can order by the number of days checked out to highlight the readers
who keep books for the longest time:
select Name, Title, ReturnedDate-CheckOutDate DaysOut
from BOOKSHELF_CHECKOUT
order by DaysOut desc;
NAME TITLE DAYSOUT

ROLAND BRANDT THE SHIPPING NEWS 59
ROLAND BRANDT THE DISCOVERERS 48
ROLAND BRANDT WEST WITH THE NIGHT 48
GERHARDT KENTGEN WONDERFUL LIFE 31
PAT LAVAY THE MISMEASURE OF MAN 31
FRED FULLER JOHN ADAMS 28
JED HOPKINS INNUMERACY 21
GERHARDT KENTGEN THE MISMEASURE OF MAN 20
FRED FULLER TRUMAN 19
EMILY TALBOT ANNE OF GREEN GABLES 18
DORAH TALBOT MY LEDGER 16
EMILY TALBOT MIDNIGHT MAGIC 14
DORAH TALBOT POLAR EXPRESS 14
DORAH TALBOT GOOD DOG, CARL 14
JED HOPKINS TO KILL A MOCKINGBIRD 14
EMILY TALBOT HARRY POTTER AND THE 11
GOBLET OF FIRE

PAT LAVAY THE SHIPPING NEWS 10
DORAH TALBOT EITHER/OR 8
GERHARDT KENTGEN MIDNIGHT MAGIC 5
But is it specific readers who are the issue, or is it that there are certain categories of books
that take longer to read? There are multiple variables involved, and looking at them in isolation
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:27 PM
Color profile: Generic CMYK printer profile
Composite Default screen
314
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:314
may lead to incorrect decisions. What is the average number of days out for the books in each
category?
select B.CategoryName,
MIN(BC.ReturnedDate-BC.CheckOutDate) MinOut,
MAX(BC.ReturnedDate-BC.CheckOutDate) MaxOut,
AVG(BC.ReturnedDate-BC.CheckOutDate) AvgOut
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
group by B.CategoryName;
CATEGORYNAME MINOUT MAXOUT AVGOUT

ADULTFIC 10 59 27.6666667
ADULTNF 16 48 29.1111111
ADULTREF 8 8 8
CHILDRENFIC 5 18 12

CHILDRENPIC 14 14 14
This is more useful, but it doesn’t factor in the impact of the different people checking out
books in the categories. To accomplish that, you could use an additional level of grouping, but
it will be easier to use if you create a
cross-tab
report. The following listing generates a report
that shows the minimum, maximum, and average days out by person by category. This query
uses the DECODE function to perform the calculations.
column CategoryName format a11
select B.CategoryName,
MAX(DECODE(BC.Name, 'FRED FULLER',
BC.ReturnedDate-BC.CheckOutDate,NULL)) MaxFF,
AVG(DECODE(BC.Name, 'FRED FULLER',
BC.ReturnedDate-BC.CheckOutDate,NULL)) AvgFF,
MAX(DECODE(BC.Name, 'DORAH TALBOT',
BC.ReturnedDate-BC.CheckOutDate,NULL)) MaxDT,
AVG(DECODE(BC.Name, 'DORAH TALBOT',
BC.ReturnedDate-BC.CheckOutDate,NULL)) AvgDT,
MAX(DECODE(BC.Name, 'GERHARDT KENTGEN',
BC.ReturnedDate-BC.CheckOutDate,NULL)) MaxGK,
AVG(DECODE(BC.Name, 'GERHARDT KENTGEN',
BC.ReturnedDate-BC.CheckOutDate,NULL)) AvgGK
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
group by B.CategoryName;
CATEGORYNAM MAXFF AVGFF MAXDT AVGDT MAXGK AVGGK

ADULTFIC
ADULTNF 28 23.5 16 16 31 25.5
ADULTREF 8 8

CHILDRENFIC 5 5
CHILDRENPIC 14 14
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:27 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
315
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:315
The output now shows the borrowers across the top—Fred Fuller’s maximum checkout time
is the MaxFF column, Dorah Talbot’s is the MaxDT column, and Gerhardt Kentgen’s is the
MaxGK column. The output shows that the AdultNF category has the longest average checkout
time for each of the borrowers shown, and that in Gerhardt Kentgen’s case it is significantly longer
than the average checkout time in the other category he checked out.
How was this report generated? In the query, the grouping is by CategoryName. The MaxFF
column query is shown here:
select B.CategoryName,
MAX(DECODE(BC.Name, 'FRED FULLER',
BC.ReturnedDate-BC.CheckOutDate,NULL)) MaxFF,
DECODE is performing an if-then check on the data: If the BC.Name column value in a row
is ‘FRED FULLER’, then calculate the difference between the ReturnedDate and CheckOutDate,
else return a NULL. That list of values is then evaluated and the maximum value is returned. A
similar set of operations returns the average checkout time for Fred Fuller:
AVG(DECODE(BC.Name, 'FRED FULLER',
BC.ReturnedDate-BC.CheckOutDate,NULL)) AvgFF,
Replacing Values via DECODE
In the last example, DECODE was used to return values conditionally, depending on the Name

of the person who checked out a book. You can also use DECODE to replace values in a list.
For example, selecting the category names from BOOKSHELF yields:
select distinct CategoryName from BOOKSHELF;
CATEGORYNAM

ADULTFIC
ADULTNF
ADULTREF
CHILDRENFIC
CHILDRENNF
CHILDRENPIC
To replace these names, you could join BOOKSHELF to CATEGORY and select the
ParentCategory and SubCategory from the CATEGORY table. If the list of categories is static,
you could avoid the join to the CATEGORY table and perform the replacement via a DECODE
function, as shown in the following listing. Note how DECODE supports multiple
if
-
then
combinations within a single call.
select distinct
DECODE(CategoryName,'ADULTFIC','Adult Fiction',
'ADULTNF','Adult Nonfiction',
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:27 PM
Color profile: Generic CMYK printer profile
Composite Default screen
316
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i

: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:316
'ADULTREF','Adult Reference',
'CHILDRENFIC','Children Fiction',
'CHILDRENNF','Children Nonfiction',
'CHILDRENPIC','Children Picturebook',
CategoryName)
from BOOKSHELF;
DECODE(CATEGORYNAME,

Adult Fiction
Adult Nonfiction
Adult Reference
Children Fiction
Children Nonfiction
Children Picturebook
In this case, the data is static; for volatile data, hard-coding the translations into the application
code would not be an acceptable programming practice. The technique shown here is useful for
transaction processing systems in which you are trying to minimize the number of database calls
performed. In this example, there is no database access required to change ‘ADULTNF’ to ‘Adult
Nonfiction’; the change occurs within the DECODE function call. Note that if there are any other
categories found, the
else
condition in the DECODE function returns the original CategoryName
column value.
DECODE within DECODE
You can DECODE function calls within other DECODE function calls. Let’s say you want to
charge late fees, with different late fee rates for different categories of books. Adult category
books may be kept later, but the penalty for late days will be higher.
Start with a basic flat rate charge of $0.20 per day for books checked out for more than

14 days:
column Name format a16
column Title format a20 word_wrapped
column DaysOut format 999.99 heading 'Days Out'
column DaysLate format 999.99 heading 'Days Late'
set pagesize 60
break on Name
select Name, Title, ReturnedDate,
ReturnedDate-CheckoutDate as DaysOut /*Count days*/,
ReturnedDate-CheckoutDate -14 DaysLate,
(ReturnedDate-CheckoutDate -14)*0.20 LateFee
from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
order by Name, CheckoutDate;
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:28 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
317
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:317
NAME TITLE RETURNEDD Days Out Days Late LATEFEE

DORAH TALBOT MY LEDGER 03-MAR-02 16.00 2.00 .4
EMILY TALBOT ANNE OF GREEN GABLES 20-JAN-02 18.00 4.00 .8
FRED FULLER JOHN ADAMS 01-MAR-02 28.00 14.00 2.8
TRUMAN 20-MAR-02 19.00 5.00 1

GERHARDT KENTGEN WONDERFUL LIFE 02-FEB-02 31.00 17.00 3.4
THE MISMEASURE OF 05-MAR-02 20.00 6.00 1.2
MAN
JED HOPKINS INNUMERACY 22-JAN-02 21.00 7.00 1.4
PAT LAVAY THE MISMEASURE OF 12-FEB-02 31.00 17.00 3.4
MAN
ROLAND BRANDT THE SHIPPING NEWS 12-MAR-02 59.00 45.00 9
THE DISCOVERERS 01-MAR-02 48.00 34.00 6.8
WEST WITH THE NIGHT 01-MAR-02 48.00 34.00 6.8
For books in the Adult categories, increase the allowed days late to 21 days. This won’t
change the number of days out, but will change the DaysLate column calculation. Since
CategoryName is not a column in BOOKSHELF_CHECKOUT, this modification also requires
the addition of the BOOKSHELF table to the from clause.
select BC.Name, BC.Title, BC.ReturnedDate,
BC.ReturnedDate-BC.CheckoutDate as DaysOut /*Count days*/,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
BC.ReturnedDate-BC.CheckoutDate -21,
BC.ReturnedDate-BC.CheckoutDate -14 ) DaysLate,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
(BC.ReturnedDate-BC.CheckoutDate -21)*0.30,
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
and BC.ReturnedDate-BC.CheckoutDate >
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',21,14)
order by BC.Name, BC.CheckoutDate;
NAME TITLE RETURNEDD Days Out Days Late LATEFEE

EMILY TALBOT ANNE OF GREEN GABLES 20-JAN-02 18.00 4.00 .8
FRED FULLER JOHN ADAMS 01-MAR-02 28.00 7.00 2.1

GERHARDT KENTGEN WONDERFUL LIFE 02-FEB-02 31.00 10.00 3
PAT LAVAY THE MISMEASURE OF 12-FEB-02 31.00 10.00 3
MAN
ROLAND BRANDT THE SHIPPING NEWS 12-MAR-02 59.00 38.00 11.4
THE DISCOVERERS 01-MAR-02 48.00 27.00 8.1
WEST WITH THE NIGHT 01-MAR-02 48.00 27.00 8.1
In the select clause, the query logic for the DaysLate column is
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
BC.ReturnedDate-BC.CheckoutDate -21,
BC.ReturnedDate-BC.CheckoutDate -14 ) DaysLate
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:28 PM
Color profile: Generic CMYK printer profile
Composite Default screen
318
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:318
In DECODE’s if-then-else format, this says “If the first five characters of the CategoryName
column match the string ‘ADULT’, then subtract 21 from the number of days late; otherwise,
subtract 14 days.” The LateFee calculation uses similar logic. The where clause uses a DECODE
to determine the limiting value for the rows returned—for ADULT category books, allow 21 days;
otherwise, allow 14:
and BC.ReturnedDate-BC.CheckoutDate >
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',21,14)
Now add an additional rule: For Adult fiction category books, the late fee will be double the
late fee for other Adult books. From the last query, the calculation for LateFee is:
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',

(BC.ReturnedDate-BC.CheckoutDate –21)*0.30,
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
The new rule requires an additional category check within the DECODE already performed.
The new LateFee calculation is shown here:
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate –21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate –21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
Reading through the preceding listing, the first DECODE function tells Oracle that if the
first five letters of the CategoryName are ‘ADULT’, then perform the second DECODE function.
The second DECODE tells Oracle that if letters 6, 7, and 8 of the CategoryName are ‘FIC’, then
the late fee is $0.60 per day after the twenty-first day; otherwise, it is $0.30 per day after the
twenty-first day. At that point, the inner DECODE function completes. For the first DECODE
function, the else clause (for non-ADULT category books) then specifies the late fee calculation.
The query and result are shown in the following listing; you can see the impact by comparing
the LateFee column for ‘THE SHIPPING NEWS’ in this report and the last report.
select BC.Name, BC.Title, BC.ReturnedDate,
BC.ReturnedDate-BC.CheckoutDate as DaysOut /*Count days*/,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
BC.ReturnedDate-BC.CheckoutDate -21,
BC.ReturnedDate-BC.CheckoutDate -14 ) DaysLate,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate -21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate -21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
and BC.ReturnedDate-BC.CheckoutDate >

P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:28 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
319
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:319
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',21,14)
order by BC.Name, BC.CheckoutDate;
NAME TITLE RETURNEDD Days Out Days Late LATEFEE

EMILY TALBOT ANNE OF GREEN GABLES 20-JAN-02 18.00 4.00 .8
FRED FULLER JOHN ADAMS 01-MAR-02 28.00 7.00 2.1
GERHARDT KENTGEN WONDERFUL LIFE 02-FEB-02 31.00 10.00 3
PAT LAVAY THE MISMEASURE OF 12-FEB-02 31.00 10.00 3
MAN
ROLAND BRANDT THE SHIPPING NEWS 12-MAR-02 59.00 38.00 22.8
THE DISCOVERERS 01-MAR-02 48.00 27.00 8.1
WEST WITH THE NIGHT 01-MAR-02 48.00 27.00 8.1
You can nest DECODEs within other DECODEs to support complex logic within your data
processing. For example, you may choose to print one column if it has a non-NULL value, and
a second column if the first is NULL. With DECODE, that is a simple function call:
DECODE(Column1, NULL, Column2, Column1)
If Column1 is NULL, Column2 will be returned; otherwise, Column1’s non-NULL value will
be returned. You could also use NVL or the Oracle9i COALESCE and NULLIF functions to
perform similar logic.
COALESCE will return the first non-NULL value encountered in a list of values. The last

DECODE example could be rewritten as
COALESCE(Column1, Column2)
Since COALESCE is a new function and its name does not clearly convey its usage, be sure
to provide comments within your code to explain the logical tests performed.
Greater Than and Less Than in DECODE
DECODE supports logic checks, but how do you do numeric comparisons in this format? The
simplest solution is often to use the SIGN function. SIGN returns a 1 if the number is positive, 0
if it is 0, and –1 if the number is negative. Because SIGN operates on numbers, you can evaluate
any function that returns a number, including date arithmetic.
Let’s modify the LateFee business rule again. Using the same base calculation, we’ll modify
the outcome so that we will not collect late fees that are less than or equal to $4.00. Here is the
original LateFee calculation:
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate –21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate –21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:29 PM
Color profile: Generic CMYK printer profile
Composite Default screen
320
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:320
If that value is less than 4, we will return a 0; otherwise, we will return the calculated value.
To implement this requirement, subtract 4 from the LateFee value; if the result is positive (its
SIGN value is 1), return that result; otherwise, return a 0.

DECODE(SIGN(
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate –21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate –21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) –4),
1,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate –21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate –21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ),
0) LateFee
Building from a simple foundation, this series of DECODE function calls allows you to
perform very complex logic on the LateFee calculations. The first DECODE evaluates the SIGN
of the next DECODE function result when 4 is subtracted from it. If that number is positive, the
calculated late fee is returned; otherwise, a 0 is returned. The following listing shows this
calculation and the output that results.
select BC.Name, BC.Title, BC.ReturnedDate,
BC.ReturnedDate-BC.CheckoutDate as DaysOut /*Count days*/,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
BC.ReturnedDate-BC.CheckoutDate -21,
BC.ReturnedDate-BC.CheckoutDate -14 ) DaysLate,
DECODE(SIGN(
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate -21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate -21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) -4),
1,

DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
DECODE(SUBSTR(CategoryName,6,3),'FIC',
(BC.ReturnedDate-BC.CheckoutDate -21)*0.60,
(BC.ReturnedDate-BC.CheckoutDate -21)*0.30),
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ),
0) LateFee
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
and BC.ReturnedDate-BC.CheckoutDate >
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',21,14)
order by BC.Name, BC.CheckoutDate;
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:29 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
321
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:321
NAME TITLE RETURNEDD Days Out Days Late LATEFEE

EMILY TALBOT ANNE OF GREEN GABLES 20-JAN-02 18.00 4.00 0
FRED FULLER JOHN ADAMS 01-MAR-02 28.00 7.00 0
GERHARDT KENTGEN WONDERFUL LIFE 02-FEB-02 31.00 10.00 0
PAT LAVAY THE MISMEASURE OF 12-FEB-02 31.00 10.00 0
MAN
ROLAND BRANDT THE SHIPPING NEWS 12-MAR-02 59.00 38.00 22.8
THE DISCOVERERS 01-MAR-02 48.00 27.00 8.1

WEST WITH THE NIGHT 01-MAR-02 48.00 27.00 8.1
You can eliminate the display of the first four rows (with $0 late fees) by making a similar
modification to the where clause.
Using CASE
As of Oracle9i, you can use the CASE function in place of DECODE. The CASE function uses
the keywords when, then, else, and end to indicate the logic path followed, which may make
the resulting code easier to follow than an equivalent DECODE.
Consider this simple DECODE example from earlier in this chapter:
select distinct
DECODE(CategoryName,'ADULTFIC','Adult Fiction',
'ADULTNF','Adult Nonfiction',
'ADULTREF','Adult Reference',
'CHILDRENFIC','Children Fiction',
'CHILDRENNF','Children Nonfiction',
'CHILDRENPIC','Children Picturebook',
CategoryName)
from BOOKSHELF;
The equivalent CASE function is
select distinct
CASE CategoryName
when 'ADULTFIC' then 'Adult Fiction'
when 'ADULTNF' then 'Adult Nonfiction'
when 'ADULTREF' then 'Adult Reference'
when 'CHILDRENFIC' then 'Children Fiction'
when 'CHILDRENNF' then 'Children Nonfiction'
when 'CHILDRENPIC' then 'Children Picturebook'
else CategoryName
end
from BOOKSHELF;
CASECATEGORYNAMEWHEN


Adult Fiction
Adult Nonfiction
Adult Reference
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:30 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Children Fiction
Children Nonfiction
Children Picturebook
CASE evaluates the first when clause and returns a match if the limiting condition is met. As
with DECODE, you can nest CASE function calls. How would you perform the LateFee column
calculations using CASE?
The DECODE function began by calculating a higher rate for ADULT category books:
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
(BC.ReturnedDate-BC.CheckoutDate –21)*0.30,
(BC.ReturnedDate-BC.CheckoutDate -14)*0.20 ) LateFee
The CASE equivalent is
CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then (BC.ReturnedDate-BC.CheckoutDate –21)*0.30
else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end
The second rule (ADULTFIC books have doubled late fees) requires a nested CASE command:
CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then
CASE SUBSTR(CategoryName,6,3)
when 'FIC' then (BC.ReturnedDate-BC.CheckoutDate –21)*0.60
else (BC.ReturnedDate-BC.CheckoutDate –21)*0.30
end

else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end
This is more complex, but the logic is very easy to follow, and will usually be simpler to
maintain than the equivalent DECODE clause. Now consider the final condition: If the calculated
late fee is less than or equal to $4, return a 0. Since we are using the CASE function, we do not
need to use a SIGN function—we can just use a “<“ comparison as part of the command processing.
The following example shows how to add this check: An additional CASE function is added, and
the inner CASE function call is enclosed in parentheses. The result is compared to $4:
CASE when
(CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then
CASE SUBSTR(CategoryName,6,3)
when 'FIC' then (BC.ReturnedDate-BC.CheckoutDate –21)*0.60
else (BC.ReturnedDate-BC.CheckoutDate –21)*0.30
end
else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end)
< 4 then 0
322
Part II: SQL and SQL*Plus
ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:322
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:30 PM
Color profile: Generic CMYK printer profile
Composite Default screen
Chapter 17: DECODE and CASE: if, then, and else in SQL
323

ORACLE Series TIGHT / Oracle9
i
: The Complete Reference / Loney, Koch / 222521-1 / Chapter 17
Blind Folio 17:323
The else clause for this CASE function call is the same as the inner CASE execution—it calculates
the late fee.
else
CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then
CASE SUBSTR(CategoryName,6,3)
when 'FIC' then (BC.ReturnedDate-BC.CheckoutDate –21)*0.60
else (BC.ReturnedDate-BC.CheckoutDate –21)*0.30
end
else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end
end
The full query is shown in the following listing, along with its output.
column LateFee format 999.99
select BC.Name, BC.Title, BC.ReturnedDate,
BC.ReturnedDate-BC.CheckoutDate as DaysOut /*Count days*/,
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',
BC.ReturnedDate-BC.CheckoutDate -21,
BC.ReturnedDate-BC.CheckoutDate -14 ) DaysLate,
CASE when
(CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then
CASE SUBSTR(CategoryName,6,3)
when 'FIC' then (BC.ReturnedDate-BC.CheckoutDate -21)*0.60
else (BC.ReturnedDate-BC.CheckoutDate -21)*0.30
end

else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end)
< 4 then 0 else
CASE SUBSTR(CategoryName,1,5)
when 'ADULT' then
CASE SUBSTR(CategoryName,6,3)
when 'FIC' then (BC.ReturnedDate-BC.CheckoutDate -21)*0.60
else (BC.ReturnedDate-BC.CheckoutDate -21)*0.30
end
else (BC.ReturnedDate-BC.CheckoutDate -14)*0.20
end
end AS LateFee
from BOOKSHELF_CHECKOUT BC, BOOKSHELF B
where BC.Title = B.Title
and BC.ReturnedDate-BC.CheckoutDate >
DECODE(SUBSTR(CategoryName,1,5), 'ADULT',21,14)
order by BC.Name, BC.CheckoutDate;
P:\010Comp\Oracle8\521-1\CD\Ventura\book.vp
Friday, July 19, 2002 4:12:30 PM
Color profile: Generic CMYK printer profile
Composite Default screen

×