Here’s the join version of Listing 8.33: SELECT DISTINCT pub_name FROM publishers p INNER JOIN titles t ON p.pub_id = t.pub_id AND type = ‘biography’; Listing 8.33 List the names of the
Trang 1Listing 8.31 uses a correlated subquery to
imitate a GROUP BYclause and list all books
that have a price greater than the average
for books of its type For each possible value
oft1, the DBMS evaluates the subquery and
includes the row in the result if the price
value in that row exceeds the calculated
average It’s unnecessary to group by type
explicitly, because the rows for which the
average price is calculated are restricted by
the subquery’s WHEREclause See Figure 8.31
for the result
Listing 8.32 uses the same structure as
Listing 8.31 to list all the books whose sales
are less than the best-selling books of their
types See Figure 8.32 for the result.
✔ Tips
■ If a subquery returns more than one row,
you can use ALLorANYto modify the
comparison operator, or you can
intro-duce the subquery with IN (ALL,ANY, and
INare covered later in this chapter.)
■ MySQL 4.0 and earlier don’t
support subqueries; see the DBMS Tip in “Understanding Subqueries”
earlier in this chapter
Listing 8.31 List all books that have a price greater
than the average for books of its type See Figure 8.31 for the result.
SELECT type, title_id, price FROM titles t1
WHERE price >
(SELECT AVG(t2.price) FROM titles t2 WHERE t1.type = t2.type) ORDER BY type ASC, title_id ASC;
Listing
type title_id price - -biography T06 19.95 biography T07 23.95 children T09 13.95 history T13 29.99 psychology T04 12.99
Figure 8.31 Result of Listing 8.31.
Listing 8.32 List all the books whose sales are
less than the best-selling books of their types See Figure 8.32 for the result.
SELECT type, title_id, sales FROM titles t1
WHERE sales <
(SELECT MAX(sales) FROM titles t2 WHERE t1.type = t2.type AND sales IS NOT NULL) ORDER BY type ASC, title_id ASC;
Listing
type title_id sales -biography T06 11320 biography T12 100001 children T08 4095 history T01 566 history T02 9566 psychology T04 13001 psychology T11 94123
Trang 2Testing Set Membership
with IN
“List Filtering with IN” in Chapter 4 describes how to use the INkeyword in a WHEREclause
to compare a literal, column value, or more-complex expression to a list of values You
also can use a subquery to generate the list
The important characteristics of a subquery set membership test are:
◆ INworks the same way with the values
in a subquery result as it does with a
parenthesized list of values (see “List
Filtering with IN” in Chapter 4)
◆ The subquery can be simple or correlated (see “Simple and Correlated Subqueries” earlier in this chapter)
◆ The subquery’s SELECT-clause list
can include only one expression or
column name
◆ The compared values must have the
same data type or must be implicitly
convertible to the same type (see
“Converting Data Types with CAST()”
in Chapter 5)
◆ String comparisons are case insensitive
or case sensitive, depending on your
DBMS; see the DBMS Tip in “Filtering
Rows with WHERE” in Chapter 4
◆ The subquery must return exactly one
column and zero or more rows A
sub-query that returns more than one column will cause an error
◆ You can use NOT INto reverse the effect
of the INtest If you specify NOT IN, the
DBMS takes the action specified by the
SQL statement if there is no matching
value in the subquery’s result
Trang 3To test set membership:
◆ In the WHEREclause of a SELECT
state-ment, type:
WHERE test_expr [NOT] IN (subquery)
test_expr is a literal value, a column
name, an expression, or a subquery that
returns a single value; and subquery is a
subquery that returns one column and
zero or more rows
If the value of test_expr equals any value
returned by subquery, the INcondition
evaluates to true The INcondition is false
if the subquery result is empty, if no row
in the subquery result matches test_expr,
or if all the values in the subquery result
are null Specify NOTto negate the
condi-tion’s result
The same syntax applies to a HAVINGclause:
HAVING test_expr [NOT] IN (subquery)
Listing 8.33 lists the names of the publishers
that have published biographies The DBMS
evaluates this statement in two steps First,
the inner query returns the IDs of the
pub-lishers that have published biographies (P01
and P03) Second, the DBMS substitutes these
values into the outer query, which finds
the names that go with the IDs in the table
publishers See Figure 8.33 for the result.
Here’s the join version of Listing 8.33:
SELECT DISTINCT pub_name
FROM publishers p
INNER JOIN titles t
ON p.pub_id = t.pub_id
AND type = ‘biography’;
Listing 8.33 List the names of the publishers that have
published biographies See Figure 8.33 for the result.
SELECT pub_name FROM publishers WHERE pub_id IN
(SELECT pub_id FROM titles WHERE type = 'biography');
Listing
pub_name -Abatis Publishers Schadenfreude Press
Figure 8.33 Result of Listing 8.33.
Listing 8.34 List the names of the publishers that
haven’t published biographies See Figure 8.34 for the result.
SELECT pub_name FROM publishers WHERE pub_id NOT IN
(SELECT pub_id FROM titles WHERE type = 'biography');
Listing
pub_name -Core Dump Books Tenterhooks Press
Figure 8.34 Result of Listing 8.34.
Trang 4Listing 8.34 is the same as Listing 8.33,
except that it uses NOT INto list the names
of the publishers that haven’t published
biographies See Figure 8.34 for the result.
This statement can’t be converted to a join
The analogous not-equal join has a different meaning: It lists the names of publishers
that have published some book that isn’t
a biography
Listing 8.35 is equivalent to Listing 7.31 in
Chapter 7, except that it uses a subquery instead of an outer join to list the authors who haven’t written (or cowritten) a book
See Figure 8.35 for the result.
Listing 8.36 lists the names of the authors
who have published a book with publisher P03 (Schadenfreude Press) The join to the table authorsis necessary to include the authors’ names (not just their IDs) in the
result See Figure 8.36 for the result.
Listing 8.35 List the authors who haven’t written (or
cowritten) a book See Figure 8.35 for the result.
SELECT au_id, au_fname, au_lname
FROM authors
WHERE au_id NOT IN
(SELECT au_id
FROM title_authors);
Listing
au_id au_fname au_lname
- -
-A07 Paddy O'Furniture
Figure 8.35 Result of Listing 8.35.
Listing 8.36 List the names of the authors who have
published a book with publisher P03 See Figure 8.36
for the result.
SELECT DISTINCT a.au_id, au_fname, au_lname
FROM title_authors ta
INNER JOIN authors a
ON ta.au_id = a.au_id
WHERE title_id IN
(SELECT title_id
FROM titles
WHERE pub_id = 'P03');
Listing
au_id au_fname au_lname
- -
-A01 Sarah Buchman
A02 Wendy Heydemark
A04 Klee Hull
Figure 8.36 Result of Listing 8.36.
Trang 5A subquery can itself include one or more
subqueries Listing 8.37 lists the names of
authors who have participated in writing at
least one biography The innermost query
returns the title IDs T06, T07, T10, and T12
The DBMS evaluates the subquery at the
next higher level by using these title IDs and
returns the author IDs Finally, the
outer-most query uses the author IDs to find the
names of the authors See Figure 8.37 for
the result
Excessive subquery nesting makes a
state-ment hard to read; often, it’s easier to restate
the query as a join Here’s the join version of
Listing 8.37:
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM authors a
INNER JOIN title_authors ta
ON a.au_id = ta.au_id
INNER JOIN titles t
ON t.title_id = ta.title_id
WHERE type = ‘biography’;
Listing 8.38 lists the names of all
non-lead authors (au_order > 1) who live in
California and who receive less than 50
percent of the royalties for a book See
Figure 8.38 for the result.
Here’s the join version of Listing 8.38:
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM authors a
INNER JOIN title_authors ta
ON a.au_id = ta.au_id
WHERE state = ‘CA’
AND royalty_share < 0.5
AND au_order > 1;
Listing 8.37 List the names of authors who have
participated in writing at least one biography See Figure 8.37 for the result.
SELECT au_id, au_fname, au_lname FROM authors
WHERE au_id IN
(SELECT au_id FROM title_authors WHERE title_id IN
(SELECT title_id FROM titles WHERE type = 'biography'));
Listing
au_id au_fname au_lname - - -A02 Wendy Heydemark A04 Klee Hull
Figure 8.37 Result of Listing 8.37.
Listing 8.38 List the names of all ancillary authors
who live in California and who receive less than 50 percent of the royalties for a book See Figure 8.38 for the result.
SELECT au_id, au_fname, au_lname FROM authors
WHERE state = 'CA' AND au_id IN
(SELECT au_id FROM title_authors WHERE royalty_share < 0.5 AND au_order > 1);
Listing
au_id au_fname au_lname - -A03 Hallie Hull A04 Klee Hull
Trang 6Listing 8.39 lists the names of authors
who are coauthors of a book To determine whether an author is a coauthor or the sole author of a book, examine his or her royalty share for the book If the royalty share is less than 100 percent (1.0), the author is a coauthor; otherwise, he or she is the sole
author See Figure 8.39 for the result.
Listing 8.40 uses a correlated subquery
to list the names of authors who are sole authors of a book—that is, authors who earn
100 percent (1.0) of the royalty on a book
See Figure 8.40 for the result The DBMS
considers each row in the outer-query table authorsto be a candidate for inclusion in the result When the DBMS examines the first candidate row in authors, it sets the correlation variable a.au_idequal to A01 (Sarah Buchman), which it substitutes into the inner query:
SELECT royalty_share FROM title_authors ta WHERE ta.au_id = ‘A01’;
The inner query returns 1.0, so the outer query evaluates to:
SELECT a.au_id, au_fname, au_lname FROM authors a
WHERE 1.0 IN (1.0)
TheWHEREcondition is true, so author A01 is included in the result The DBMS repeats this procedure for every author; see “Simple and Correlated Subqueries” earlier in this chapter
Listing 8.39 List the names of authors who are
coauthors of a book See Figure 8.39 for the result.
SELECT au_id, au_fname, au_lname
FROM authors a
WHERE au_id IN
(SELECT au_id
FROM title_authors
WHERE royalty_share < 1.0);
Listing
au_id au_fname au_lname
- -
-A02 Wendy Heydemark
A03 Hallie Hull
A04 Klee Hull
A06 Kellsey
Figure 8.39 Result of Listing 8.39.
Listing 8.40 List the names of authors who are sole
authors of a book See Figure 8.40 for the result.
SELECT a.au_id, au_fname, au_lname
FROM authors a
WHERE 1.0 IN
(SELECT royalty_share
FROM title_authors ta
WHERE ta.au_id = a.au_id);
Listing
au_id au_fname au_lname
-
-A01 Sarah Buchman
A02 Wendy Heydemark
A04 Klee Hull
A05 Christian Kells
A06 Kellsey
Figure 8.40 Result of Listing 8.40.
Trang 7Listing 8.41 lists the names of authors who
are both coauthors and sole authors The
inner query returns the author IDs of sole
authors, and the outer query compares these
IDs with the IDs of the coauthors See
Figure 8.41 for the result.
You can rewrite Listing 8.41 as a join or as
an intersection Here’s the join version of
Listing 8.41:
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM authors a
INNER JOIN title_authors ta1
ON a.au_id = ta1.au_id
INNER JOIN title_authors ta2
ON a.au_id = ta2.au_id
WHERE ta1.royalty_share < 1.0
AND ta2.royalty_share = 1.0;
Here’s the intersection version of Listing 8.41
(see “Finding Common Rows with
INTERSECT” in Chapter 9):
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM authors a
INNER JOIN title_authors ta
ON a.au_id = ta.au_id
WHERE ta.royalty_share < 1.0
INTERSECT
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM authors a
INNER JOIN title_authors ta
ON a.au_id = ta.au_id
WHERE ta.royalty_share = 1.0;
Listing 8.41 List the names of authors who are both
coauthors and sole authors See Figure 8.41 for the result.
SELECT DISTINCT a.au_id, au_fname, au_lname FROM authors a
INNER JOIN title_authors ta
ON a.au_id = ta.au_id WHERE ta.royalty_share < 1.0
AND a.au_id IN
(SELECT a.au_id FROM authors a INNER JOIN title_authors ta
ON a.au_id = ta.au_id AND ta.royalty_share = 1.0 );
Listing
au_id au_fname au_lname - - -A02 Wendy Heydemark A04 Klee Hull A06 Kellsey
Figure 8.41 Result of Listing 8.41.
Listing 8.42 List the types of books common to more
than one publisher See Figure 8.42 for the result.
SELECT DISTINCT t1.type FROM titles t1 WHERE t1.type IN
(SELECT t2.type FROM titles t2 WHERE t1.pub_id <> t2.pub_id );
Listing
type -biography history
Trang 8Listing 8.42 uses a correlated subquery to
list the types of books published by more than
one publisher See Figure 8.42 for
the result
Here’s the self-join version of Listing 8.42:
SELECT DISTINCT t1.type
FROM titles t1
INNER JOIN titles t2
ON t1.type = t2.type
AND t1.pub_id <> t2.pub_id;
✔ Tips
■ INis equivalent to = ANY; see “Comparing
Some Subquery Values with ANY” later in
this chapter
■ NOT INis equivalent to <> ALL(not<>
ANY); see “Comparing All Subquery
Values with ALL” later in this chapter
■ To run Listing 8.41 in
Microsoft Access, type:
SELECT DISTINCT a.au_id, au_fname,
au_lname
FROM (authors AS a
INNER JOIN title_authors AS ta1
ON a.au_id = ta1.au_id)
INNER JOIN title_authors AS ta2
ON a.au_id = ta2.au_id
WHERE ta1.royalty_share < 1.0
AND ta2.royalty_share = 1.0;
MySQL 4.0 and earlier don’t support
subqueries; see the DBMS Tip in
“Understanding Subqueries” earlier in
this chapter
In older PostgreSQL versions,
con-vert the floating-point numbers in
Listings 8.38 through 8.41 to DECIMAL;
see “Converting Data Types with CAST()”
in Chapter 5 To run Listings 8.38 through 8.41, change the floating-point literals
to (Listing 8.38):
CAST(0.5 AS DECIMAL) and (Listing 8.39):
CAST(1.0 AS DECIMAL) and (Listing 8.40):
CAST(1.0 AS DECIMAL) and (Listing 8.41):
CAST(1.0 AS DECIMAL)(in two places) Some DBMSs let you test multiple values simultaneously by using this syntax:
SELECT columns FROM table1 WHERE (col1, col2, , colN) IN (SELECT colA, colB, , colN FROM table2);
The test expression (left of IN) is a
paren-thesized list of table1 columns The
subquery returns the same number of columns as there are in the list The DBMS compares the values in correspon-ding columns The following query, for
example, works in Oracle, DB2, MySQL, and PostgreSQL:
SELECT au_id, city, state FROM authors
WHERE (city, state) IN (SELECT city, state FROM publishers);
The result lists the authors who live in the same city and state as some publisher:
au_id city state
————— ————————————— —————
A03 San Francisco CA A04 San Francisco CA A05 New York NY
Trang 9Comparing All Subquery
Values with ALL
You can use the ALLkeyword to determine
whether a value is less than or greater than
all the values in a subquery result.
The important characteristics of subquery
comparisons that use ALLare:
◆ ALLmodifies a comparison operator in a
subquery comparison test and follows =,
<>,<,<=,>, or >=; see “Comparing a
Subquery Value by Using a Comparison
Operator” earlier in this chapter
◆ The combination of a comparison
opera-tor and ALLtells the DBMS how to apply
the comparison test to the values returned
by a subquery < ALL, for example, means
less than every value in the subquery
result, and > ALLmeans greater than
every value in the subquery result
◆ When ALLis used with <,<=,>, or >=, the
comparison is equivalent to evaluating
the subquery result’s minimum or
maxi-mum value < ALLmeans less than every
subquery value—in other words, less
than the minimum value > ALLmeans
greater than every subquery value—that
is, greater than the maximum value
Table 8.2 shows equivalent ALL
expres-sions and column functions Listing 8.45
later in this section shows how to
repli-cate a > ALLquery by using MAX()
◆ Semantic equivalence doesn’t mean that
two queries will run at the same speed.
For example, the query SELECT * FROM table1 WHERE col1 > ANY (SELECT MAX(col1) FROM table2); usually is faster than
SELECT * FROM table1 WHERE col1 > ALL (SELECT col1 FROM table2); For more information, see “Comparing Equivalent Queries” later in this chapter
◆ The comparison = ALLis valid but isn’t often used = ALLalways will be false unless all the values returned by the subquery are identical (and equal to the test value)
◆ The subquery can be simple or correlated (see “Simple and Correlated Subqueries” earlier in this chapter)
◆ The subquery’s SELECT-clause list can include only one expression or column name
◆ The compared values must have the same data type or must be implicitly convertible to the same type (see
“Converting Data Types with CAST()”
in Chapter 5)
◆ String comparisons are case insensitive
or case sensitive, depending on your DBMS; see the DBMS Tip in “Filtering Rows with WHERE” in Chapter 4
◆ The subquery must return exactly one column and zero or more rows A sub-query that returns more than one column will cause an error
◆ If the subquery returns no rows, the ALL condition is true (You might find this result to be counterintuitive.)
Table 8.2
ALL Equivalencies
A L L E x p r e s s i o n C o l u m n F u n c t i o n
< ALL(subquery) < MIN(subquery values)
> ALL(subquery) > MAX(subquery values)
Trang 10To compare all subquery values:
◆ In the WHEREclause of a SELECT state-ment, type:
WHERE test_expr op ALL (subquery)
test_expr is a literal value, a column
name, an expression, or a subquery
that returns a single value; op is a
com-parison operator (=,<>,<,<=,>, or >=);
and subquery is a subquery that returns
one column and zero or more rows
TheALLcondition evaluates to true if
all values in subquery satisfy the ALL con-dition or if the subquery result is empty (has zero rows) The ALLcondition is false
if any (at least one) value in subquery
doesn’t satisfy the ALLcondition or if any value is null
The same syntax applies to a HAVINGclause:
HAVING test_expr op ALL (subquery)
Listing 8.43 lists the authors who live in a
city in which no publisher is located The inner query finds all the cities in which publishers are located, and the outer query compares each author’s city to all the
pub-lishers’ cities See Figure 8.43 for the result.
You can use NOT INto replicate Listing 8.43:
SELECT au_id, au_lname, au_fname, city FROM authors
WHERE city NOT IN (SELECT city FROM publishers);
Listing 8.44 lists the nonbiographies that
are priced less than all the biographies The inner query finds all the biography prices
The outer query inspects the lowest price in the list and determines whether each
non-biography is cheaper See Figure 8.44 for the
result The price IS NOT NULLcondition is required because the price of biography T10
is null Without this condition, the entire
Listing 8.43 List the authors who live in a city in which
no publisher is located See Figure 8.43 for the result.
SELECT au_id, au_lname, au_fname, city
FROM authors
WHERE city <> ALL
(SELECT city
FROM publishers);
Listing
au_id au_lname au_fname city
- -
-A01 Buchman Sarah Bronx
A02 Heydemark Wendy Boulder
A06 Kellsey Palo Alto
A07 O'Furniture Paddy Sarasota
Figure 8.43 Result of Listing 8.43.
Listing 8.44 List the nonbiographies that are cheaper
than all the biographies See Figure 8.44 for the result.
SELECT title_id, title_name
FROM titles
WHERE type <> 'biography'
AND price < ALL
(SELECT price
FROM titles
WHERE type = 'biography'
AND price IS NOT NULL );
Listing
title_id title_name
-
-T05 Exchange of Platitudes
T08 Just Wait Until After School
T11 Perhaps It's a Glandular Problem
Figure 8.44 Result of Listing 8.44.