Type --- --- ---CITY NOT NULL VARCHAR213 SAMPLEDATE NOT NULL DATE NOON NUMBER3,1 MIDNIGHT NUMBER3,1 PRECIPITATION NUMBER To add a new row to this table, use this: insert into COMFORT va
Trang 1A third option, full outer join, returns all rows from both tables Rows that do not satisfy the
on condition return NULL values In this example, there are no rows to satisfy this condition, so
the query returns the same 31 rows as the right outer join.
select B.Title, MAX(BC.ReturnedDate - BC.CheckoutDate)
"Most Days Out"
from BOOKSHELF_CHECKOUT BC full outer join BOOKSHELF B
on BC.Title = B.Title group by B.Title;
Prior to Oracle9i, you can generate the full outer join results by performing two separate
outer joins—using each table as the outer table—and using a union operation to combine the
results in a single query
Replacing NOT IN with an Outer Join
The various logical tests that can be done in a where clause all have their separate performance
measures A NOT IN test may force a full read of the table in the subquery select For example,
what books were not checked out? You could write a query like this:
select Title
from BOOKSHELF
where Title not in
(select Title from BOOKSHELF_CHECKOUT) order by Title;
TITLE
-BOX SOCIALS
CHARLOTTE'S WEB
COMPLETE POEMS OF JOHN KEATS
EMMA WHO SAVED MY LIFE
GOSPEL
JOURNALS OF LEWIS AND CLARK
KIERKEGAARD ANTHOLOGY
LETTERS AND PAPERS FROM PRISON
PREACHING TO HEAD AND HEART
RUNAWAY BUNNY
SHOELESS JOE
THE COST OF DISCIPLESHIP
THE GOOD BOOK
TRUMPET OF THE SWAN
UNDER THE EYE OF THE CLOCK
This is typically the way such a query would be written, even though experienced Oracleusers know it may be slow—you may be forcing Oracle to perform a time-intensive full table
scan on the BOOKSHELF_CHECKOUT table The optimizer may internally transform that NOT
IN to one of the following functionally identical approaches The following query uses an outer
Trang 2join and produces the same result The difference is that this one will be efficient because the
optimizer can take advantage of indexes on the join columns:
select distinct B.Title
from BOOKSHELF_CHECKOUT BC right outer join BOOKSHELF B
on BC.Title = B.Title where BC.Title is NULL
COMPLETE POEMS OF JOHN KEATS
EMMA WHO SAVED MY LIFE
GOSPEL
JOURNALS OF LEWIS AND CLARK
KIERKEGAARD ANTHOLOGY
LETTERS AND PAPERS FROM PRISON
PREACHING TO HEAD AND HEART
RUNAWAY BUNNY
SHOELESS JOE
THE COST OF DISCIPLESHIP
THE GOOD BOOK
TRUMPET OF THE SWAN
UNDER THE EYE OF THE CLOCK
Why does it work and give the same results as the NOT IN? The outer join between the two
tables ensures that all rows are available for the test, including those titles for whom no checkout
records are listed in the BOOKSHELF_CHECKOUT table The line
where BC.Title is NULL
produces only those titles that don’t appear in the BOOKSHELF_CHECKOUT table (and are
therefore returned as NULL titles by Oracle) The logic here is obscure, but it works The best
way to use this technique is simply to follow the model
Replacing NOT IN with NOT EXISTS
A more common way of performing this type of query requires using the NOT EXISTS clause.
NOT EXISTS is typically used to determine which values in one table do not have matching
values in another table In usage, it is identical to the EXISTS clause; in the following example,
you’ll see the difference in the query logic and the records returned
NOT EXISTS allows you to use a correlated subquery to eliminate from a table all records
that may successfully be joined to another table For this example, that means you can eliminate
from the BOOKSHELF table all titles that are present in the Title column of the BOOKSHELF_
CHECKOUT table The following query shows how this is done:
select B.Title
from BOOKSHELF B
Trang 3where not exists
(select 'x' from BOOKSHELF_CHECKOUT BC where BC.Title = B.Title)
COMPLETE POEMS OF JOHN KEATS
EMMA WHO SAVED MY LIFE
GOSPEL
JOURNALS OF LEWIS AND CLARK
KIERKEGAARD ANTHOLOGY
LETTERS AND PAPERS FROM PRISON
PREACHING TO HEAD AND HEART
RUNAWAY BUNNY
SHOELESS JOE
THE COST OF DISCIPLESHIP
THE GOOD BOOK
TRUMPET OF THE SWAN
UNDER THE EYE OF THE CLOCK
This query shows the books that have not been checked out, as previously shown via the NOT
IN and outer join methods How does this query work?
For each record in the BOOKSHELF table, the NOT EXISTS subquery is checked If the join of
that record to the BOOKSHELF_CHECKOUT table returns a row, then the results of the subquery
EXIST NOT EXISTS tells the query to reverse that return code; therefore, any row in BOOKSHELF
that can be successfully joined to BOOKSHELF_CHECKOUT will not be returned by the outer query
The only rows left are the BOOKSHELF rows that do not have a matching row in BOOKSHELF_
CHECKOUT
NOT EXISTS is a very efficient way to perform this type of query, especially when multiple columns are used for the join Because it uses a join, NOT EXISTS is frequently able to use
available indexes, whereas NOT IN may not be able to use those indexes The ability to use
indexes for this type of query can have a dramatic impact on the query’s performance
Natural and Inner Joins
You can use the natural keyword to indicate that a join should be performed based on all columns
that have the same name in the two tables being joined For example, what titles in BOOK_ORDER
match those already in BOOKSHELF?
Trang 4The natural join returned the results as if you had typed in the following:
select BO.Title
from BOOK_ORDER BO, BOOKSHELF
where BO.Title = BOOKSHELF.Title
and BO.Publisher = BOOKSHELF.Publisher and BO.CategoryName = BOOKSHELF.CategoryName;
The join was performed based on the columns the two tables had in common
Support for inner join syntax was introduced in Oracle9i Inner joins are the default—theyreturn the rows the two tables have in common, and are the alternative to outer joins Note that
they support the on and using clauses, so you can specify your join criteria as shown in the following
UNION, INTERSECT, and MINUS
Sometimes you need to combine information of a similar type from more than one table A classic
example of this is merging two or more mailing lists prior to a mailing campaign Depending on the
purpose of a particular mailing, you might want to send letters to any of these combinations of people:
■ Everyone in both lists (while avoiding sending two letters to someone who happens to
be in both lists)
■ Only those people who are in both lists
■ Those people in only one of the lists
These three combinations of lists are known in Oracle as UNION, INTERSECT, and MINUS.
In the following examples, you will see how to use these three clauses to manage the results of
multiple queries The examples will compare the books on hand (BOOKSHELF) with those on
Trang 5select Title from BOOK_ORDER;
If we UNION them together, how many rows are returned?
select Title from BOOKSHELF
Where did the extra record go? The problem is that one of the Title values in BOOK_ORDER
is already in the BOOKSHELF table To show the duplicates, use UNION ALL instead of UNION:
select Title from BOOKSHELF
Trang 6The duplicate title is now listed twice.
In the following, the two lists of books are intersected This list contains only those namesthat are inboth underlying tables (note that the restriction on Title < ‘M%’ has been eliminated
for this example):
select Title from BOOKSHELF
Next, the list of new books (in BOOK_ORDER but not already in BOOKSHELF) is generated,
via the MINUS operator:
select Title from BOOK_ORDER
You could have also used MINUS to show which books had not been checked out:
select Title from BOOKSHELF
minus
Trang 7select Title from BOOKSHELF_CHECKOUT;
TITLE
-BOX SOCIALS
CHARLOTTE'S WEB
COMPLETE POEMS OF JOHN KEATS
EMMA WHO SAVED MY LIFE
GOSPEL
JOURNALS OF LEWIS AND CLARK
KIERKEGAARD ANTHOLOGY
LETTERS AND PAPERS FROM PRISON
PREACHING TO HEAD AND HEART
RUNAWAY BUNNY
SHOELESS JOE
THE COST OF DISCIPLESHIP
THE GOOD BOOK
TRUMPET OF THE SWAN
UNDER THE EYE OF THE CLOCK
15 rows selected.
You’ve just learned the basics of UNION, INTERSECT, and MINUS Now let’s go into detail.
In combining two tables, Oracle does not concern itself with column names on either side of the
combination operator—that is, Oracle will require that each select statement be valid and have
valid columns for its own table(s), but the column names in the first select statement do not have
to be the same as those in the second Oracle does have these stipulations:
■ The select statements must have the same number of columns If the two tables being
queried have differing numbers of columns selected, you can select strings in place ofcolumns to make the two queries’ column lists match
■ The corresponding columns in the select statements must be the same datatype (they
needn’t be the same length)
When ordering the output, Oracle uses the column names from the first select statement in giving the query results Consequently, only column names from the first select statement can be
used in the order by.
You can use combination operators with two or more tables, but when you do, precedence
becomes an issue, especially if INTERSECT and MINUS appear Use parentheses to force the order
you want
IN Subqueries
Combination operators can be used in subqueries, but you must be careful with precedence
A query of the form
select ColA from TABLE_A
where ColA in
Trang 8(select Col1 from TABLE_1)
union
(select Col2 from TABLE_2);
is poorly written and ambiguous Which will be performed first, the union of the two queries as
part of a single where clause, or the in clause based on the query of TABLE_1, followed by a union
of that result with TABLE_2? Use parentheses to clarify your meaning and enforce the proper
precedence of operations The in clause is always given a higher precedence than union, unless
you use parentheses to alter the way the query is executed If you want the union to have higher
precedence, use parentheses:
select ColA from TABLE_A
where ColA in
(select Col1 from TABLE_1
union
select Col2 from TABLE_2);
Restrictions on UNION, INTERSECT, and MINUS
Queries that use UNION, INTERSECT, or MINUS in their where clause must have the same
number and type of columns in their select list Note that the equivalent IN construction does
not have that limitation
The use of combination operators in place of IN, AND, and OR is a matter of personal style Most SQL users regard IN, AND, and OR as being clearer and easier to understand than
combination operators
Trang 914 Some Complex
Possibilities
Trang 10T his chapter continues the study of the more complex Oracle functions and features.Of particular interest here is the creation of simple and group queries that can be
turned into views, the use of totals in calculations, and the creation of reports showing tree structure Like the techniques covered in Chapter 13, these techniques are not essential for most reporting needs If they look overly difficult, don’t be frightened off If you are new to Oracle and the use of its query facilities, it is enough to know
that these capabilities exist and you can turn to them if needed
Complex Groupings
Views can build upon each other In Chapter 12, you saw the concept of creating a view of a
grouping of rows from a table As shown in Chapter 12, you can easily join views to other views
and tables to produce additional views to simplify the tasks of querying and reporting
As your groupings grow more complex, you will find that views are invaluable to your coding efforts; they simplify the representation of data at different grouping levels within your application
They also make it easier to use the more advanced analytic functions available
Consider the CATEGORY_COUNT view, first encountered in Chapter 12:
create or replace view CATEGORY_COUNT as
select CategoryName, COUNT(*) as Counter
from BOOKSHELF
group by CategoryName;
select * from CATEGORY_COUNT;
CATEGORYNAME COUNTER
-ADULTFIC 6
ADULTNF 10
ADULTREF 6
CHILDRENFIC 5
CHILDRENNF 1
CHILDRENPIC 3
Let’s order the results by their Counter column values, with the highest first: select * from CATEGORY_COUNT order by Counter desc; CATEGORYNAME COUNTER
-ADULTNF 10
ADULTFIC 6
ADULTREF 6
CHILDRENFIC 5
CHILDRENPIC 3
CHILDRENNF 1
Trang 11The output shows the ranking of the categories; the ADULTNF category ranks first in terms ofthe number of books Without displaying this list, you could determine where a different Counter
value would be in the rankings To do this, we’ll use the RANK function As shown in the following
listing, the RANK function takes a value as its input and has additional clauses—the within group
and order by clauses—that tell Oracle how to do the ranking Where would a Counter value of
3 rank?
select RANK(3) within group
(order by Counter desc)
from CATEGORY_COUNT;
RANK(3)WITHINGROUP(ORDERBYCOUNTERDESC)
-5
A Counter value of 3 would be the fifth-highest Counter value How about a Counter value of 8?
select RANK(8) within group
(order by Counter desc)
from CATEGORY_COUNT;
RANK(8)WITHINGROUP(ORDERBYCOUNTERDESC)
-2
Adding those five books to the category would move it up to second place From a percentile
perspective, what would the ranking for that category be?
select PERCENT_RANK(8) within group
(order by Counter desc)
from CATEGORY_COUNT;
PERCENT_RANK(8)WITHINGROUP(ORDERBYCOUNTERDESC)
-.166666667
As expected, it would be in the top one-sixth of the categories
With this technique of using both summary views and analytic functions, you can createviews and reports that include weighted average, effective yield, percentage of total, percentage
of subtotal, and many similar calculations There is no effective limit to how many views can be
built on top of each other, although even the most complex calculations seldom require more
than three or four levels of views built upon views Note that you can also create inline views in
the from clause, as shown in Chapter 12.
Using Temporary Tables
You can create a table that exists solely for your session or whose data persists for the duration of
your transaction You can use temporary tables to support specialized rollups or specific
application-processing requirements
Trang 12To create a temporary table, use the create global temporary table command When you create a temporary table, you can specify whether it should last for the duration of your session (via the on
commit preserve rows clause) or whether its rows should be deleted when the transaction completes
(via the on commit delete rows clause).
Unlike a permanent table, a temporary table does not automatically allocate space when it iscreated Space will be dynamically allocated for the table as rows are inserted:
create global temporary table YEAR_ROLLUP (
Year NUMBER(4), Month VARCHAR2(9), Counter NUMBER)
on commit preserve rows;
You can see the duration of your data in YEAR_ROLLUP by querying the Duration column
of USER_TABLES for this table In this case, the value of Duration is SYS$SESSION If on commit
delete rows had been specified instead, the Duration value would be SYS$TRANSACTION.
Now that the YEAR_ROLLUP table exists, you can populate it, such as via an insert as select
command with a complex query You can then query the YEAR_ROLLUP table as part of a join
with other tables You may find this method simpler to implement than the methods shown earlier
Using ROLLUP, GROUPING, and CUBE
How can you perform grouping operations, such as totals, within a single SQL statement rather than
via SQL*Plus commands? You can use the ROLLUP and CUBE functions to enhance the grouping
actions performed within your queries Let’s see how this enables us to manage the data related to
book returns The book loaner program has become more popular, so the loan time is now limited
to 14 days, with a $0.20 fee per extra day The following report shows the late charges by person:
set headsep !
column Name format a20
column Title format a20 word_wrapped
column DaysOut format 999.99 heading 'Days!Out'
column DaysLate format 999.99 heading 'Days!Late'
break on Name skip 1 on report
compute sum of LateFee on Name
set linesize 80
set pagesize 60
set newpage 0
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;
Trang 13We can eliminate the DaysOut display and focus on the late fees, showing the fees due on each
of the return dates:
clear compute
clear break
select ReturnedDate, Name,
SUM((ReturnedDate-CheckoutDate -14)*0.20) LateFee from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
group by ReturnedDate, Name
order by ReturnedDate, Name;
Trang 14RETURNEDD NAME LATEFEE
-
-20-JAN-02 EMILY TALBOT .8
22-JAN-02 JED HOPKINS 1.4
02-FEB-02 GERHARDT KENTGEN 3.4
12-FEB-02 PAT LAVAY 3.4
01-MAR-02 FRED FULLER 2.8
01-MAR-02 ROLAND BRANDT 13.6
03-MAR-02 DORAH TALBOT .4
05-MAR-02 GERHARDT KENTGEN 1.2
12-MAR-02 ROLAND BRANDT 9
20-MAR-02 FRED FULLER 1
Then we can modify it further to group the late fees by month:
select TO_CHAR(ReturnedDate,'MONTH'), Name,
SUM((ReturnedDate-CheckoutDate -14)*0.20) LateFee from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
group by TO_CHAR(ReturnedDate,'MONTH'), Name;
TO_CHAR(R NAME LATEFEE
-
-FEBRUARY PAT LAVAY 3.4
FEBRUARY GERHARDT KENTGEN 3.4
JANUARY JED HOPKINS 1.4
JANUARY EMILY TALBOT .8
MARCH FRED FULLER 3.8
MARCH DORAH TALBOT .4
MARCH ROLAND BRANDT 22.6
MARCH GERHARDT KENTGEN 1.2
Instead of simply grouping by Month and Name, you can use the ROLLUP function to generate subtotals and totals In the following example, the group by clause is modified to include a ROLLUP
function call Notice the additional rows generated at the end of the result set and after each month:
select TO_CHAR(ReturnedDate,'MONTH'), Name,
SUM((ReturnedDate-CheckoutDate -14)*0.20) LateFee from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
group by ROLLUP(TO_CHAR(ReturnedDate,'MONTH'), Name);
TO_CHAR(R NAME LATEFEE
-
-FEBRUARY PAT LAVAY 3.4
FEBRUARY GERHARDT KENTGEN 3.4
FEBRUARY 6.8
JANUARY JED HOPKINS 1.4
JANUARY EMILY TALBOT .8
JANUARY 2.2
Trang 15MARCH FRED FULLER 3.8
MARCH DORAH TALBOT .4
MARCH ROLAND BRANDT 22.6
MARCH GERHARDT KENTGEN 1.2
MARCH 28
37
For each month, Oracle has calculated the total late fee, and shows it with a NULL name
value The output shows two separate charges of $3.40 in February, and a monthly total of $6.80
For the quarter, the total of the late charges is $37.00 You could have calculated these via SQL*Plus
commands (see Chapter 6), but this method allows you to generate these sums via a single SQL
command regardless of the tool used to query the database
Let’s refine the appearance of the report You can use the GROUPING function to determine whether the row is a total or subtotal (generated by ROLLUP) or corresponds to a NULL value in
the database In the select clause, the Name column will be selected as follows:
select DECODE(GROUPING(Name),1, 'All names',Name),
The GROUPING function will return a value of 1 if the column’s value is generated by a ROLLUP action This query uses DECODE (discussed at length in Chapter 16) to evaluate the
result of the GROUPING function If the GROUPING output is 1, the value was generated by the
ROLLUP function, and Oracle will print the phrase ‘All names’; otherwise, it will print the value
of the Name column We will apply similar logic to the Date column The full query is shown in
the following listing, along with its output:
select DECODE(GROUPING(TO_CHAR(ReturnedDate,'MONTH')),1,
'All months',TO_CHAR(ReturnedDate,'MONTH')), DECODE(GROUPING(Name),1, 'All names',Name), SUM((ReturnedDate-CheckoutDate -14)*0.20) LateFee from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
group by ROLLUP(TO_CHAR(ReturnedDate, 'MONTH'), Name);
DECODE(GRO DECODE(GROUPING(NAME),1,' LATEFEE
-
-FEBRUARY PAT LAVAY 3.4
FEBRUARY GERHARDT KENTGEN 3.4
FEBRUARY All names 6.8
JANUARY JED HOPKINS 1.4
JANUARY EMILY TALBOT 8
JANUARY All names 2.2
MARCH FRED FULLER 3.8
MARCH DORAH TALBOT 4
MARCH ROLAND BRANDT 22.6
MARCH GERHARDT KENTGEN 1.2
MARCH All names 28
All months All names 37
Trang 16You can use the CUBE function to generate subtotals for all combinations of the values in the group by clause The following query uses the CUBE function to generate this information:
select DECODE(GROUPING(TO_CHAR(ReturnedDate,'MONTH')),1,
'All months',TO_CHAR(ReturnedDate,'MONTH')), DECODE(GROUPING(Name),1, 'All names',Name), SUM((ReturnedDate-CheckoutDate -14)*0.20) LateFee from BOOKSHELF_CHECKOUT
where ReturnedDate-CheckoutDate > 14
group by CUBE(TO_CHAR(ReturnedDate,'MONTH'), Name);
DECODE(GRO DECODE(GROUPING(NAME),1,' LATEFEE
-
-All months -All names 37
All months PAT LAVAY 3.4
All months FRED FULLER 3.8
All months JED HOPKINS 1.4
All months DORAH TALBOT 4
All months EMILY TALBOT 8
All months ROLAND BRANDT 22.6
All months GERHARDT KENTGEN 4.6
FEBRUARY All names 6.8
FEBRUARY PAT LAVAY 3.4
FEBRUARY GERHARDT KENTGEN 3.4
JANUARY All names 2.2
JANUARY JED HOPKINS 1.4
JANUARY EMILY TALBOT 8
MARCH All names 28
MARCH FRED FULLER 3.8
MARCH DORAH TALBOT 4
MARCH ROLAND BRANDT 22.6
MARCH GERHARDT KENTGEN 1.2
The CUBE function provided the summaries generated by the ROLLUP option, plus it shows
the sums by Name for the ‘All months’ category Being able to perform these summaries in standard
SQL greatly enhances your ability to pick the best reporting tool for your users
Family Trees and connect by
One of Oracle’s more interesting but little used or understood facilities is its connect by clause.
Put simply, this method is used to report, in order, the branches of afamily tree Such trees are
encountered often—the genealogy of human families, livestock, horses; corporate management,
company divisions, manufacturing; literature, ideas, evolution, scientific research, theory, and
even views built upon views
The connect by clause provides a means to report on all of the family members in any of
these many trees It lets you exclude branches or individual members of a family tree, and allows
you to travel through the tree either up or down, reporting on the family members encountered
during the trip
Trang 17The earliest ancestor in the tree is technically called theroot node In everyday English, thiswould be called thetrunk Extending from the trunk are branches, which have other branches,
which have still other branches The forks where one or more branches split away from a larger
branch are callednodes, and the very end of a branch is called a leaf, or a leaf node Figure 14-1
(above) shows a picture of such a tree
The following is a table of cows and bulls born between January 1900 and October 1908 Aseach offspring is born, it is entered as a row in the table, along with its sex, parents (the cow and
bull), and birth date If you compare the cows and offspring in this table with Figure 14-1, you’ll
find they correspond EVE has no recorded cow or bull parent because she was born on a different
farm, and ADAM and BANDIT are bulls brought in for breeding, again with no parents in the table
column Cow format a6
column Bull format a6
column Offspring format a10
column Sex format a3
select * from BREEDING
order by Birthdate;
FIGURE 14-1. Eve’s descendants
Trang 18OFFSPRING SEX COW BULL BIRTHDATE
- - - -
-BETSY F EVE ADAM 02-JAN-00
POCO M EVE ADAM 15-JUL-00
GRETA F EVE BANDIT 12-MAR-01
MANDY F EVE POCO 22-AUG-02
CINDY F EVE POCO 09-FEB-03
NOVI F BETSY ADAM 30-MAR-03
GINNY F BETSY BANDIT 04-DEC-03
DUKE M MANDY BANDIT 24-JUL-04
TEDDI F BETSY BANDIT 12-AUG-05
SUZY F GINNY DUKE 03-APR-06
PAULA F MANDY POCO 21-DEC-06
RUTH F GINNY DUKE 25-DEC-06
DELLA F SUZY BANDIT 11-OCT-08
to 5 for DELLA, that is really thegeneration If EVE is the first generation of cattle, then DELLA is
the fifth generation Whenever the connect by clause is used, the Level column can be used in the
select statement to discover the generation of each row Level is apseudo-column like SysDate
and User It’s not really a part of the table, but it is available under specific circumstances The next
listing shows an example of using Level
The results of this query are apparent in the following table, but why did the select statement
produce this? How does it work?
column Offspring format a30
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE F EVE ADAM BETSY F 02-JAN-00
BETSY ADAM NOVI F 30-MAR-03
BETSY BANDIT GINNY F 04-DEC-03
GINNY DUKE SUZY F 03-APR-06
SUZY BANDIT DELLA F 11-OCT-08
GINNY DUKE RUTH F 25-DEC-06
BETSY BANDIT TEDDI F 12-AUG-05
EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
MANDY BANDIT DUKE M 24-JUL-04
MANDY POCO PAULA F 21-DEC-06
EVE POCO CINDY F 09-FEB-03
Trang 19Note that this is really Figure 14-1 turned clockwise onto its side EVE isn’t centered, but she
is the root node (trunk) of this tree Her children are BETSY, POCO, GRETA, MANDY, and CINDY
BETSY’s children are NOVI, GINNY, and TEDDI GINNY’s children are SUZY and RUTH And
SUZY’s child is DELLA MANDY also has two children, DUKE and PAULA
This tree started with EVE as the first “offspring.” If the SQL statement had said start with MANDY, only MANDY, DUKE, and PAULA would have been selected start with defines the beginning of
that portion of the tree that will be displayed, and it includes only branches stretching out from
the individual that start with specifies start with acts just as its name implies.
The LPAD in the select statement is probably somewhat confusing Recall from Chapter 7 the format for LPAD:
LPAD(string,length [,'set'])
That is, take the specifiedstring and left-pad it for the specified length with the specified set of
characters If no set is specified, left-pad the string with blanks
Compare this syntax to the LPAD in the select statement shown earlier:
LPAD(' ',6*(Level-1))
In this case, thestring is a single character (a space, indicated by the literal space enclosed in single
quotation marks) Also, 6*(Level–1) is thelength, and because the set is not specified, spaces will
be used In other words, this tells SQL to take this string of one space and left-pad it to the number
of spaces determined by 6*(Level–1), a calculation made by first subtracting 1 from the Level and
then multiplying this result by 6 For EVE, the Level is 1, so 6*(1–1), or 0 spaces, is used For BETSY,
the Level (her generation) is 2, so an LPAD of 6 is used Thus, for each generation after the first,
six additional spaces will be concatenated to the left of the Offspring column The effect is obvious
in the result just shown The name of each Offspring is indented by left-padding with the number
of spaces corresponding to its Level or generation
Why is this done, instead of simply applying the LPAD directly to Offspring? For two reasons.
First, a direct LPAD on Offspring would cause the names of the offspring to be right-justified The
names at each level would end up having their last letters lined up vertically Second, if Level–1
is equal to 0, as it is for EVE, the resulting LPAD of EVE will be 0 characters wide, causing EVE
to vanish:
select Cow, Bull, LPAD(Offspring,6*(Level-1),' ') AS Offspring,
Sex, Birthdate from BREEDING start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-F EVE ADAM BETSY F 02-JAN-00
BETSY ADAM NOVI F 30-MAR-03
BETSY BANDIT GINNY F 04-DEC-03
Trang 20GINNY DUKE SUZY F 03-APR-06
SUZY BANDIT DELLA F 11-OCT-08
GINNY DUKE RUTH F 25-DEC-06
BETSY BANDIT TEDDI F 12-AUG-05
EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
MANDY BANDIT DUKE M 24-JUL-04
MANDY POCO PAULA F 21-DEC-06
EVE POCO CINDY F 09-FEB-03
Therefore, to get the proper spacing for each level, to ensure that EVE appears, and to make
the names line up vertically on the left, the LPAD should be used with the concatenation function,
and not directly on the Offspring column
Now, how does connect by work? Look again at Figure 14-1 Starting with NOVI and traveling
downward, which cows are the offspring prior to NOVI? The first is BETSY, and the offspring just
prior to BETSY is EVE Even though it is not instantly readable, the clause
connect by Cow = PRIOR Offspring
tells SQL to find the next row in which the value in the Cow column is equal to the value in the
Offspring column in the prior row Look at the table and you’ll see that this is true
Excluding Individuals and Branches
There are two methods of excluding cows from a report One uses the normal where clause
technique, and the other uses the connect by clause itself The difference is that the exclusion
using the connect by clause will exclude not just the cow mentioned, but all of its children as
well If you use connect by to exclude BETSY, then NOVI, GINNY, TEDDI, SUZY, RUTH, and
DELLA all vanish The connect by clause really tracks the tree structure If BETSY had never been
born, none of her offspring would have been either In this example, the and clause modifies the
connect by clause:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring
and Offspring != 'BETSY';
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE F EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
MANDY BANDIT DUKE M 24-JUL-04
MANDY POCO PAULA F 21-DEC-06
EVE POCO CINDY F 09-FEB-03
Trang 21The where clause removes only the cow or cows it mentions If BETSY dies, she is removed
from the chart, but her offspring are not In fact, notice that BETSY is still there under the Cow
column as the mother of her children, NOVI, GINNY, and TEDDI:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
where Offspring != 'BETSY'
start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE F BETSY ADAM NOVI F 30-MAR-03
BETSY BANDIT GINNY F 04-DEC-03
GINNY DUKE SUZY F 03-APR-06
SUZY BANDIT DELLA F 11-OCT-08
GINNY DUKE RUTH F 25-DEC-06
BETSY BANDIT TEDDI F 12-AUG-05
EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
MANDY BANDIT DUKE M 24-JUL-04
MANDY POCO PAULA F 21-DEC-06
EVE POCO CINDY F 09-FEB-03
The order in which the family tree is displayed when using connect by is basically level by
level, left to right, as shown in Figure 14-1, starting with the lowest level, Level 1 For example,
you may want to alter this order to collect the cows and their offspring by birth date:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring
order by Cow, Birthdate;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-BETSY ADAM NOVI F 30-MAR-03
BETSY BANDIT GINNY F 04-DEC-03
BETSY BANDIT TEDDI F 12-AUG-05
EVE ADAM BETSY F 02-JAN-00
EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
EVE POCO CINDY F 09-FEB-03
GINNY DUKE SUZY F 03-APR-06
GINNY DUKE RUTH F 25-DEC-06
MANDY BANDIT DUKE M 24-JUL-04
Trang 22MANDY POCO PAULA F 21-DEC-06
SUZY BANDIT DELLA F 11-OCT-08
start with Offspring = 'EVE'
connect by Cow = PRIOR Offspring
order by Birthdate;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE ADAM BETSY F 02-JAN-00
EVE ADAM POCO M 15-JUL-00
EVE BANDIT GRETA F 12-MAR-01
EVE POCO MANDY F 22-AUG-02
EVE POCO CINDY F 09-FEB-03
BETSY ADAM NOVI F 30-MAR-03
BETSY BANDIT GINNY F 04-DEC-03
MANDY BANDIT DUKE M 24-JUL-04
BETSY BANDIT TEDDI F 12-AUG-05
GINNY DUKE SUZY F 03-APR-06
MANDY POCO PAULA F 21-DEC-06
GINNY DUKE RUTH F 25-DEC-06
SUZY BANDIT DELLA F 11-OCT-08
EVE F
Now, the order of the rows no longer shows generations, as in a tree, but the indenting stillpreserves this information You can’t tell what offspring belong to which parents without looking
at the Cow and Bull columns, though Not knowing Eve’s birth date clearly disrupts the display of
the family tree
Traveling Toward the Roots
Thus far, the direction of travel in reporting on the family tree has been from parents toward
children Is it possible to start with a child, and move backward to parent, grandparent,
great-grandparent, and so on? To do so, the word prior is simply moved to the other side of the equal
sign In the following examples, the Offspring is set equal to the prior Cow value; in the earlier
examples, the Cow was set equal to the prior Offspring value The following traces DELLA’s
ancestry:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'DELLA'
connect by Offspring = PRIOR Cow;
Trang 23COW BULL OFFSPRING SEX BIRTHDATE
- - -
-SUZY BANDIT DELLA F 11-OCT-08
GINNY DUKE SUZY F 03-APR-06
BETSY BANDIT GINNY F 04-DEC-03
EVE ADAM BETSY F 02-JAN-00
EVE F
This shows DELLA’s own roots, but it’s a bit confusing if compared to the previous displays
It looks as if DELLA is the ancestor, and EVE the great-great-granddaughter Adding an order by
for Birthdate helps, but EVE is still further to the right:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'DELLA'
connect by Offspring = PRIOR Cow
order by Birthdate;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE ADAM BETSY F 02-JAN-00
BETSY BANDIT GINNY F 04-DEC-03
GINNY DUKE SUZY F 03-APR-06
SUZY BANDIT DELLA F 11-OCT-08
EVE F
The solution is simply to change the calculation in the LPAD:
select Cow, Bull, LPAD(' ',6*(5-Level))||Offspring Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'DELLA'
connect by Offspring = PRIOR Cow
order by Birthdate;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-EVE ADAM BETSY F 02-JAN-00
BETSY BANDIT GINNY F 04-DEC-03
GINNY DUKE SUZY F 03-APR-06
SUZY BANDIT DELLA F 11-OCT-08
EVE F
Finally, look how different this report is when the connect by tracks the parentage of the bull.
Here are ADAM’s offspring:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
Trang 24start with Offspring = 'ADAM'
connect by PRIOR Offspring = Bull;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-ADAM M EVE ADAM BETSY F 02-JAN-00
EVE ADAM POCO M 15-JUL-00
EVE POCO MANDY F 22-AUG-02
EVE POCO CINDY F 09-FEB-03
MANDY POCO PAULA F 21-DEC-06
BETSY ADAM NOVI F 30-MAR-03
ADAM and BANDIT were the original bulls at the initiation of the herd To create a singletree that reports both ADAM’s and BANDIT’s offspring, you would have to invent a “father” for
the two of them, which would be the root of the tree One of the advantages that these alternative
trees have over the type of tree shown earlier is that many inheritance groups—from families to
projects to divisions within companies—can be accurately portrayed in more than one way:
select Cow, Bull, LPAD(' ',6*(Level-1))||Offspring AS Offspring,
Sex, Birthdate from BREEDING
start with Offspring = 'BANDIT'
connect by PRIOR Offspring = Bull;
COW BULL OFFSPRING SEX BIRTHDATE
- - -
-BANDIT M EVE BANDIT GRETA F 12-MAR-01
BETSY BANDIT GINNY F 04-DEC-03
MANDY BANDIT DUKE M 24-JUL-04
GINNY DUKE SUZY F 03-APR-06
GINNY DUKE RUTH F 25-DEC-06
BETSY BANDIT TEDDI F 12-AUG-05
SUZY BANDIT DELLA F 11-OCT-08
The Basic Rules
Using connect by and start with to create tree like reports is not difficult, but certain basic rules
Trang 255 connect by
6 order by
■ prior forces reporting to be from the root out toward the leaves (if the prior column is the parent) or from a leaf toward the root (if the prior column is the child).
■ A where clause eliminates individuals from the tree, but not their descendants (or ancestors,
if prior is on the right side of the equal sign).
■ A qualification in the connect by (particularly a not equal) eliminates both an individual
and all of its descendants (or ancestors, depending on how you trace the tree)
■ connect by cannot be used with a table join in the where clause.
This particular set of commands is one that few people are likely to remember correctly
However, with a basic understanding of trees and inheritance, you should be able to construct
a proper select statement to report on a tree just by referring to this chapter for correct syntax.
Trang 2715
Changing Data:
insert, update, merge, and delete
Trang 28U ntil now, virtually everything you’ve learned about Oracle, SQL, and SQL*Plushas related to selecting data from tables in the database This chapter shows how
tochange the data in a table—how to insert new rows, update the values ofcolumns in rows, and delete rows entirely Although these topics have not beencovered explicitly, nearly everything you already know about SQL, including
datatypes, calculations, string formatting, where clauses, and the like, can be used here, so there
really isn’t much new to learn Oracle gives you a transparent, distributed database capability
that inserts, updates, and deletes data in remote databases as well (see Chapter 23) As you’ll see
in this chapter, Oracle allows you to combine these commands via multitable inserts and the merge
command (to perform inserts and updates in a single command)
insert
The SQL command insert lets you place a row of information directly into a table (or indirectly,
through a view) The COMFORT table tracks temperatures at noon and midnight as well as daily
precipitation, city by city, for four sample dates throughout the year:
describe COMFORT
Name Null? Type
- -
-CITY NOT NULL VARCHAR2(13)
SAMPLEDATE NOT NULL DATE
NOON NUMBER(3,1)
MIDNIGHT NUMBER(3,1)
PRECIPITATION NUMBER
To add a new row to this table, use this:
insert into COMFORT
values ('WALPOLE', '21-MAR-03',
56.7, 43.8, 0);
1 row created.
The word values must precede the list of data to be inserted A character string must be in
single quotation marks Numbers can stand by themselves Each field is separated by commas,
and the fields must be in the same order as the columns are when the table is described
A date must be in single quotation marks and in the default Oracle date format To insert a
date not in your default format, use the TO_DATE function, with a formatting mask, as shown
in the following example:
insert into COMFORT
values ('WALPOLE', TO_DATE('06/22/2003','MM/DD/YYYY'),
56.7, 43.8, 0);
1 row created.
Trang 30You cannot use the insert into…select from syntax with LONG datatypes unless you are using the TO_LOB function to insert the
LONG data into a LOB column
Of course, you don’t need to simply insert the value in a selected column You can modify the
column using any of the appropriate string, date, or number functions within the select statement.
The results of those functions are what will be inserted You can attempt to insert a value in a
column that exceeds its width (for character datatypes) or its magnitude (for number datatypes)
You have to fit the value within the constraints you defined on your columns These attempts
will produce a “value too large for column” or “mismatched datatype” error message If you now
query the COMFORT table for the city of Walpole, the results will be the records you inserted:
select * from COMFORT
where City = 'WALPOLE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
Two records are shown for ‘22-JUN-03’ because one of them has a time component Use the
TO_CHAR function to see the time:
select City, SampleDate,
TO_CHAR(SampleDate,'HH24:MI:SS') AS TimeofDay, Noon from COMFORT
where City = 'WALPOLE';
CITY SAMPLEDAT TIMEOFDA NOON
Using the APPEND Hint to Improve insert Performance
As you will see in Chapter 43, Oracle uses an optimizer to determine the most efficient way to
perform each SQL command For insert statements, Oracle tries to insert each new record into
an existing block of data already allocated to the table This execution plan optimizes the use of
space required to store the data However, it may not provide adequate performance for an insert
with a select command that inserts multiple rows You can amend the execution plan by using the
Trang 31APPEND hint to improve the performance of large inserts The APPEND hint will tell the database
to find the last block into which the table’s data has ever been inserted The new records will be
inserted starting in the next block following the last previously used block Furthermore, the inserted
data is written directly to the datafiles, bypassing the data block buffer cache As a result, there is
much less space management work for the database to do during the insert Therefore, the insert
may complete faster when the APPEND hint is used
You specify the APPEND hint within the insert command A hint looks like a comment—it
starts with /* and ends with */ The only difference is that the starting set of characters includes
a plus sign (+) before the name of the hint The following example shows an insert command
whose data is appended to the table:
insert /*+ APPEND */ into BOOKSHELF (Title)
select Title from BOOK_ORDER
where Title not in (select Title from BOOKSHELF);
The records from the BOOK_ORDER table will be inserted into the BOOKSHELF table Instead
of attempting to reuse space within the BOOKSHELF table, the new records will be placed at the
end of the table’s physical storage space
Because the new records will not attempt to reuse available space that the table has alreadyused, the space requirements for the BOOKSHELF table may increase In general, you should use
the APPEND hint only when inserting large volumes of data into tables with little reusable space
The point at which appended records will be inserted is called the table’shigh-water mark—and
the only way to reset the high-water mark is to truncate the table Because truncate will delete
all records and cannot be rolled back, you should make sure you have a backup of the table’s data
prior to performing the truncate See truncate in the Alphabetical Reference for further details.
rollback, commit, and autocommit
When you insert, update, or delete data from the database, you can reverse, orroll back, the work
you’ve done This can be very important when an error is discovered The process of committing
or rolling back work is controlled by two SQL*Plus commands: commit and rollback Additionally,
SQL*Plus has the facility to automatically commit your work without your explicitly telling it to
do so This is controlled by the autocommit feature of set Like other set features, you can show it,
like this:
show autocommit
autocommit OFF
OFF is the default, and in almost all cases it’s the preferred mode You can also specify
a number for the autocommit value; this value will determine the number of commands after
which Oracle will issue a commit Having autocommit off means insert, update, and delete
operations are not made final until you commit them:
commit;
commit complete
Trang 33rollback to savepoint B;
If you now query COMFORT, you’ll see that the last insert has been rolled back, but the others
are still there:
select * from COMFORT
where City = 'WALPOLE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
The last two records are still not committed; you need to execute a commit command or another
command to force a commit to occur You can still roll back the second insert (to savepoint A) or roll
back all the inserts (via a rollback command).
Implicit commit
The actions that will force a commit to occur, even without your instructing it to, are quit, exit
(the equivalent of quit), and any Data Definition Language (DDL) command Using any of these
commands forces a commit.
Auto rollback
If you’ve completed a series of insert, update, or delete operations but have not yet explicitly or
implicitly committed them, and you experience serious difficulties, such as a computer failure,
Oracle will automatically roll back any uncommitted work If the machine or database goes
down, Oracle does the rollback as cleanup work the next time the database is brought back up
Multitable Inserts
You can perform multiple inserts in a single command You can perform all the inserts unconditionally
or you can specify conditions—using a when clause to tell Oracle how to manage the multiple
inserts If you specify all, then all the when clauses will be evaluated; specifying first tells Oracle
to skip subsequent when clauses after it finds one that is true for the row being evaluated You can
also use an else clause to tell Oracle what to do if none of the when clauses evaluates to true.
To illustrate this functionality, let’s create a new table with a slightly different structure thanCOMFORT:
drop table COMFORT_TEST;
create table COMFORT_TEST (
City VARCHAR2(13) NOT NULL,
SampleDate DATE NOT NULL,
Trang 34Measure VARCHAR2(10),
Value NUMBER(3,1)
);
COMFORT_TEST will have multiple records for each record in COMFORT—its Measure column
will have values such as ‘Midnight’, ‘Noon’, and ‘Precip’, allowing us to store a greater number
of measures for each city on each sample date
Now populate COMFORT_TEST with data from COMFORT, unconditionally:
from COMFORT
where City = 'KEENE';
12 rows created.
This query tells Oracle to insert multiple rows for each row returned from the COMFORT table
The query of COMFORT returns four rows The first into clause inserts the Noon value, along with
a text string of ‘NOON’ in the Measure column The second into clause inserts the Midnight value,
and the third inserts the Precipitation value, as shown in the query of COMFORT_TEST following
the insert:
select * from COMFORT_TEST;
CITY SAMPLEDAT MEASURE VALUE
- -
-KEENE 21-MAR-03 NOON 39.9
KEENE 22-JUN-03 NOON 85.1
KEENE 23-SEP-03 NOON 99.8
KEENE 22-DEC-03 NOON -7.2
KEENE 21-MAR-03 MIDNIGHT -1.2
KEENE 22-JUN-03 MIDNIGHT 66.7
KEENE 23-SEP-03 MIDNIGHT 82.6
KEENE 22-DEC-03 MIDNIGHT -1.2
KEENE 21-MAR-03 PRECIP 4.4
KEENE 22-JUN-03 PRECIP 1.3
KEENE 23-SEP-03 PRECIP
KEENE 22-DEC-03 PRECIP 3.9
12 rows selected.
What if you had used the first keyword instead of the all keyword? Unless you use when clauses, you can’t use the first keyword The following example shows the use of the when clause to restrict
Trang 35which inserts are performed within the multi-insert command For this example, the all keyword
when Midnight > 70 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'MIDNIGHT', Midnight)
when Precipitation is not null then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'PRECIP', Precipitation) select City, SampleDate, Noon, Midnight, Precipitation
from COMFORT
where City = 'KEENE';
6 rows created.
What six rows were inserted? The two Noon values met this condition:
when Noon > 80 then
The one Midnight value met this condition:
when Midnight > 70 then
And the three Precipitation values met this condition:
when Precipitation is not null then
You can see the results in COMFORT_TEST:
select * from COMFORT_TEST;
CITY SAMPLEDAT MEASURE VALUE
- -
-KEENE 22-JUN-03 NOON 85.1
KEENE 23-SEP-03 NOON 99.8
KEENE 23-SEP-03 MIDNIGHT 82.6
KEENE 21-MAR-03 PRECIP 4.4
KEENE 22-JUN-03 PRECIP 1.3
KEENE 22-DEC-03 PRECIP 3.9
What if you had used first instead?
delete from COMFORT_TEST;
commit;
Trang 36insert FIRST
when Noon > 80 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'NOON', Noon)
when Midnight > 70 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'MIDNIGHT', Midnight)
when Precipitation is not null then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'PRECIP', Precipitation) select City, SampleDate, Noon, Midnight, Precipitation
from COMFORT
where City = 'KEENE';
4 rows created.
In this case, four rows are inserted:
select * from COMFORT_TEST;
CITY SAMPLEDAT MEASURE VALUE
- -
-KEENE 21-MAR-03 PRECIP 4.4
KEENE 22-DEC-03 PRECIP 3.9
KEENE 22-JUN-03 NOON 85.1
KEENE 23-SEP-03 NOON 99.8
What happened to the MIDNIGHT value? Only one record passed the MIDNIGHT when clause:
when Midnight > 70 then
This record also passed the NOON when clause:
when Noon > 80 then
Therefore, its Noon value (99.8) was inserted Because the first keyword was used, and the condition
that was evaluated first (Noon) was true, Oracle did not check the rest of the conditions for that row
The same process happened to the PRECIP measures—the other non-NULL Precipitation value was
on the same record as the Noon reading of 85.1
What if none of the conditions had been met? To show that option, let’s create a third table,COMFORT2, with the same structure as COMFORT:
create table COMFORT2 (
City VARCHAR2(13) NOT NULL,
SampleDate DATE NOT NULL,
Noon NUMBER(3,1),
Midnight NUMBER(3,1),
Precipitation NUMBER
);
Trang 37Now, we’ll execute an insert for all cities (using the first clause) along with an else clause
specifying that any rows that fail the conditions will be placed into the COMFORT2 table For this
example, the when conditions are changed to limit the number of rows that pass the conditions:
delete from COMFORT_TEST;
delete from COMFORT2;
commit;
insert FIRST
when Noon > 80 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'NOON', Noon)
when Midnight > 100 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'MIDNIGHT', Midnight)
when Precipitation > 100 then into COMFORT_TEST (City, SampleDate, Measure, Value) values (City, SampleDate, 'PRECIP', Precipitation) else
into COMFORT2 select City, SampleDate, Noon, Midnight, Precipitation
from COMFORT
where City = 'KEENE';
4 rows created.
The feedback tells you how many rows were created, but not which table they were created in
The total reported is for all inserts combined In this case, two records were inserted into COMFORT_
TEST and two were inserted into COMFORT2 because of the else condition:
select * from COMFORT_TEST;
CITY SAMPLEDAT MEASURE VALUE
- -
-KEENE 22-JUN-03 NOON 85.1
KEENE 23-SEP-03 NOON 99.8
select * from COMFORT2;
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
- - -
-KEENE 21-MAR-03 39.9 -1.2 4.4
KEENE 22-DEC-03 -7.2 -1.2 3.9
delete
Removing a row or rows from a table requires the delete command, as seen in the examples in
the last section The where clause is essential to removing only the rows you intend delete without
Trang 38a where clause will empty the table completely The following command deletes the Walpole
entries from the COMFORT table:
delete from COMFORT where City = 'WALPOLE';
Of course, a where clause in a delete, just as in an update or a select that is part of an insert, can contain as much logic as any select statement, and may include subqueries, unions, intersects,
and so on You can always roll back a bad insert, update, or delete, but you really should experiment
with select before actually making the change to the database, to make sure you are doing the
right thing
Now that you’ve just deleted the rows where City = ‘WALPOLE’, you can test the effect of
that delete with a simple query:
select * from COMFORT
where City = 'WALPOLE';
no rows selected
Now roll back the delete and run the same query:
rollback;
Rollback complete
select * from COMFORT
where City = 'WALPOLE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
This illustrates that recovery is possible, so long as a commit hasn’t occurred If the changes have
been committed, you may be able to use flashback queries (see Chapter 27) to retrieve the data
An additional command for deleting records, truncate, does not behave the same as delete.
Whereas delete allows you to commit or roll back the deletion, truncate automatically deletes all
records from the table The action of the truncate command cannot be rolled back or committed;
the truncated records are unrecoverable You cannot perform a flashback query to see data that
has been truncated See the truncate command in the Alphabetical Reference for further details.
Trang 39update requires setting specific values for each column you want to change, and specifying which
row or rows you want to affect by using a carefully constructed where clause:
update COMFORT set Precipitation = 5, Midnight = 73.1
where City = 'KEENE'
and SampleDate = '22-DEC-03';
1 row updated.
Here is the effect, shown in the 22-DEC-03 record:
select * from COMFORT
where City = 'KEENE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
any other legitimate function in setting a value for the update (just as you can for an insert, or in
the where clause of a delete) Here, each temperature in Keene is decreased by one degree:
update COMFORT set Midnight = Midnight - 1, Noon = Noon - 1
where City = 'KEENE';
4 rows updated.
Here is the effect of the update:
select * from COMFORT
where City = 'KEENE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
Trang 40If your update violates the column definitions or constraints, it will
fail In this case, setting Noon or Midnight to values of 100 or greaterwill violate the numeric scale of the columns
As with delete, the where clause is critical Without one, every row in the table will be updated.
With an improperly constructed where clause, the wrong rows will be updated, often in ways
that are hard to discover or fix, especially if your work has been committed Always set feedback
on when doing updates, and look at the feedback to be sure the number of rows updated is what
you expected it to be Query the rows after you update to see if the expected change took place.
update with Embedded select
It is possible to set values in an update by embedding a select statement right in the middle of it.
Note that this select has its own where clause, picking out the temperature for the city of Manchester
from the WEATHER table, and the update has its own where clause to affect just the city of Keene
on a certain day:
update COMFORT set Midnight =
(select Temperature from WEATHER where City = 'MANCHESTER') where City = 'KEENE'
and SampleDate = '22-DEC-03';
1 row updated.
Here is the effect of the update:
select * from COMFORT
where City = 'KEENE';
CITY SAMPLEDAT NOON MIDNIGHT PRECIPITATION
Chapter 13 for details on correlated queries
You also can use an embedded select to update multiple columns at once The columns must
be in parentheses and separated by a comma, as shown here:
update COMFORT set (Noon, Midnight) =
(select Humidity, Temperature