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

SQL VISUAL QUICKSTART GUIDE- P30 ppsx

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 (175.98 KB, 10 trang )

Using Subqueries as
Column Expressions
In Chapters 4, 5, and 6, you learned that the
items in a
SELECT
-clause list can be literals,
column names, or more-complex expressions.
SQL also lets you to embed a subquery in a
SELECT
-clause list.
A subquery that’s used as a column expres-
sion must be a scalar subquery. Recall from
Table 8.1 in “Subquery Syntax” earlier in this
chapter that a scalar subquery returns a single
value (that is, a one-row, one-column result).
In most cases, you’ll have to use an aggregate
function or restrictive
WHERE
conditions in
the subquery to guarantee that the subquery
returns only one row.
The syntax for the
SELECT
-clause list is the
same as you’ve been using all along, except
that you can specify a parenthesized sub-
query as one of the column expressions in
the list, as the following examples show.
Listing 8.17 uses two simple subqueries as
column expressions to list each biography,
its price, the average price of all books (not


just biographies), and the difference between
the price of the biography and the average
price of all books. The aggregate function
AVG()
guarantees that each subquery returns
a single value. See Figure 8.17 for the result.
Remember that
AVG()
ignores nulls when
computing an average; see “Calculating an
Average with
AVG()
” in Chapter 6.
270
Chapter 8
Using Subqueries as Column Expressions
Listing 8.17 List each biography, its price, the average
price of all books, and the difference between the price
of the biography and the average price of all books.
See Figure 8.17 for the result.
SELECT title_id,
price,
(SELECT AVG(price) FROM titles)
AS "AVG(price)",
price - (SELECT AVG(price) FROM titles)
AS "Difference"
FROM titles
WHERE type='biography';
Listing
title_id price AVG(price) Difference


T06 19.95 18.3875 1.5625
T07 23.95 18.3875 5.5625
T10 NULL 18.3875 NULL
T12 12.99 18.3875 -5.3975
Figure 8.17 Result of Listing 8.17.
Listing 8.18 uses correlated subqueries to
list all the authors of each book in one row, as
you’d view them in a report or spreadsheet.
See Figure 8.18 for the result. Note that in
each
WHERE
clause, SQL qualifies
title_id
implicitly with the table alias
ta
referenced in
the subquery’s
FROM
clause; see “Qualifying
Column Names in Subqueries” earlier in this
chapter. For a more efficient way to imple-
ment this query, see the Tips in this section.
See Listing 15.8 in Chapter 15 for the reverse
of this query.
271
Subqueries
Using Subqueries as Column Expressions
Listing 8.18 List all the authors of each book in one
row. See Figure 8.18 for the result.

SELECT title_id,
(SELECT au_id
FROM title_authors ta
WHERE au_order = 1
AND title_id = t.title_id)
AS "Author 1",
(SELECT au_id
FROM title_authors ta
WHERE au_order = 2
AND title_id = t.title_id)
AS "Author 2",
(SELECT au_id
FROM title_authors ta
WHERE au_order = 3
AND title_id = t.title_id)
AS "Author 3"
FROM titles t;
Listing
title_id Author 1 Author 2 Author 3

T01 A01 NULL NULL
T02 A01 NULL NULL
T03 A05 NULL NULL
T04 A03 A04 NULL
T05 A04 NULL NULL
T06 A02 NULL NULL
T07 A02 A04 NULL
T08 A06 NULL NULL
T09 A06 NULL NULL
T10 A02 NULL NULL

T11 A06 A03 A04
T12 A02 NULL NULL
T13 A01 NULL NULL
Figure 8.18 Result of Listing 8.18.
In Listing 8.19, I revisit Listing 7.30 in
“Creating Outer Joins with
OUTER JOIN
” in
Chapter 7, but this time, I’m using a corre-
lated subquery instead of an outer join to
list the number of books that each author
wrote (or cowrote). See Figure 8.19 for
the result.
Listing 8.20 uses a correlated subquery to
list each author and the latest date on which
he or she published a book. You should qualify
every column name explicitly in a subquery
that contains a join to make it clear which
table is referenced (even when qualifiers are
unnecessary). See Figure 8.20 for the result.
272
Chapter 8
Using Subqueries as Column Expressions
Listing 8.19 List the number of books that each
author wrote (or cowrote), including authors who
have written no books. See Figure 8.19 for the result.
SELECT au_id,
(SELECT COUNT(*)
FROM title_authors ta
WHERE ta.au_id = a.au_id)

AS "Num books"
FROM authors a
ORDER BY au_id;
Listing
au_id Num books

A01 3
A02 4
A03 2
A04 4
A05 1
A06 3
A07 0
Figure 8.19 Result of Listing 8.19.
Listing 8.20 List each author and the latest date on
which he or she published a book. See Figure 8.20 for
the result.
SELECT au_id,
(SELECT MAX(pubdate)
FROM titles t
INNER JOIN title_authors ta
ON ta.title_id = t.title_id
WHERE ta.au_id = a.au_id)
AS "Latest pub date"
FROM authors a;
Listing
au_id Latest pub date

A01 2000-08-01
A02 2000-08-31

A03 2000-11-30
A04 2001-01-01
A05 2000-09-01
A06 2002-05-31
A07 NULL
Figure 8.20 Result of Listing 8.20.
Listing 8.21 uses a correlated subquery to
compute the running total of all book sales.
A running total, or running sum, is a com-
mon calculation: For each book, I want to
compute the sum of all sales of the books
that precede the book. Here, I’m defining
precede to mean those books whose
title_id
comes before the current book’s
title_id
alphabetically. Note the use of table aliases
to refer to the same table in two contexts.
The subquery returns the sum of sales for all
books preceding the current book, which is
denoted by
t1.title_id
. See Figure 8.21 for
the result. See also “Calculating Running
Statistics” in Chapter 9.
✔ Tips

You also can use a subquery in a
FROM
clause. In the Tips in “Aggregating Distinct

Values with
DISTINCT
” in Chapter 6, I used
a
FROM
subquery to replicate a distinct
aggregate function. Listing 8.22 uses a
FROM
subquery to calculate the greatest
number of titles written (or cowritten)
by any author. See Figure 8.22 for the
result. Note that the outer query uses
a table alias (
ta
) and column label
(
count_titles
) to reference the inner
query’s result. See also the “Column
Aliases and
WHERE
” sidebar in “Filtering
Rows with
WHERE
” in Chapter 4.

You also can use a subquery as a column
expression in
UPDATE
,

INSERT
, and
DELETE
statements (see Chapter 10) but not in
an
ORDER BY
list.
continues on next page
273
Subqueries
Using Subqueries as Column Expressions
Listing 8.21 Compute the running sum of all book
sales. See Figure 8.21 for the result.
SELECT t1.title_id, t1.sales,
(SELECT SUM(t2.sales)
FROM titles t2
WHERE t2.title_id <= t1.title_id)
AS “Running total”
FROM titles t1;
Listing
title_id sales Running total

T01 566 566
T02 9566 10132
T03 25667 35799
T04 13001 48800
T05 201440 250240
T06 11320 261560
T07 1500200 1761760
T08 4095 1765855

T09 5000 1770855
T10 NULL 1770855
T11 94123 1864978
T12 100001 1964979
T13 10467 1975446
Figure 8.21 Result of Listing 8.21.
Listing 8.22 Calculate the greatest number of titles
written (or cowritten) by any author. See Figure 8.22
for the result.
SELECT MAX(ta.count_titles) AS “Max titles”
FROM (SELECT COUNT(*) AS count_titles
FROM title_authors
GROUP BY au_id) ta;
Listing
Max titles

4
Figure 8.22 Result of Listing 8.22.

Use
CASE
expressions instead of correlated
subqueries to implement Listing 8.18 more
efficiently (see “Evaluating Conditional
Values with
CASE
” in Chapter 5):
SELECT title_id,
MIN(CASE au_order WHEN 1
THEN au_id

END)
AS “Author 1”,
MIN(CASE au_order WHEN 2
THEN au_id
END)
AS “Author 2”,
MIN(CASE au_order WHEN 3
THEN au_id
END)
AS “Author 3”
FROM title_authors
GROUP BY title_id
ORDER BY title_id ASC;

MySQL 4.0 and earlier don’t
support subqueries; see the
DBMS Tip in “Understanding Subqueries”
earlier in this chapter.
In Microsoft Access, you must increase
the precision of the average-price calcula-
tion in Listing 8.17. Use the type-conversion
function
CDbl()
to coerce the average
price to a double-precision floating-point
number; see the DBMS Tip in “Converting
Data Types with
CAST()
” in Chapter 5. To
run Listing 8.17, change both occurrences

of
AVG(price)
to
CDbl(AVG(price))
.
274
Chapter 8
Using Subqueries as Column Expressions
Comparing a Subquery
Value by Using a
Comparison Operator
You can use a subquery as a filter in a
WHERE
clause or
HAVING
clause by using one of the
comparison operators (
=
,
<>
,
<
,
<=
,
>
, or
>=
).
The important characteristics of a subquery

comparison test are:

The comparison operators work the
same way as they do in other compar-
isons (refer to Table 4.2 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 a single value
(a one-row, one-column result). A sub-
query that returns more than one value
will cause an error.

If the subquery result contains zero
rows, the comparison test will evaluate
to false.
275
Subqueries
Comparing a Subquery Value
The hard part of writing these statements is
getting the subquery to return one value,
which you can guarantee several ways:

Using an aggregate function on an
ungrouped table always returns a single
value (see Chapter 6).

Using a join with the outer query based
on a key always returns a single value.
To compare a subquery value:

In the
WHERE
clause of a
SELECT
state-
ment, type:

WHERE test_expr op (subquery)
test_expr is a literal value, a column name,
an expression, or a subquery that returns
a single value; op is a comparison operator
(
=
,
<>
,
<
,
<=
,
>
, or
>=
); and subquery is a
scalar subquery that returns exactly one
column and zero or one rows.
If the value returned by subquery satisfies
the comparison to test_expr, the compar-
ison condition evaluates to true. The
comparison condition is false if the sub-
query value doesn’t satisfy the condition,
the subquery value is null, or the subquery
result is empty (has zero rows).
The same syntax applies to a
HAVING
clause:
HAVING test_expr op (subquery)

Listing 8.23 tests the result of a simple
subquery for equality to list the authors
who live in the state in which Tenterhooks
Press is located. Only one publisher is
named Tenterhooks Press, so the inner
WHERE
condition guarantees that the inner
query returns a single-valued result. See
Figure 8.23 for the result.
276
Chapter 8
Comparing a Subquery Value
Listing 8.23 List the authors who live in the state in
which the publisher Tenterhooks Press is located. See
Figure 8.23 for the result.
SELECT au_id, au_fname, au_lname, state
FROM authors
WHERE state =
(SELECT state
FROM publishers
WHERE pub_name = 'Tenterhooks Press');
Listing
au_id au_fname au_lname state

A03 Hallie Hull CA
A04 Klee Hull CA
A06 Kellsey CA
Figure 8.23 Result of Listing 8.23.
Listing 8.24 List the authors who live in the state in
which the publisher XXX is located. See Figure 8.24

for the result.
SELECT au_id, au_fname, au_lname, state
FROM authors
WHERE state =
(SELECT state
FROM publishers
WHERE pub_name = 'XXX');
Listing
au_id au_fname au_lname state

Figure 8.24 Result of Listing 8.24 (an empty table).
Listing 8.24 is the same as Listing 8.23
except for the name of the publisher. No
publisher named XXX exists, so the sub-
query returns an empty table (zero rows).
The comparison evaluates to null, so the
final result is empty. See Figure 8.24 for
the result.
Listing 8.25 lists the books with above-
average sales. Subqueries introduced with
comparison operators often use aggregate
functions to return a single value. See
Figure 8.25 for the result.
To list the authors of the books with above-
average sales, I’ve added an inner join to
Listing 8.25 (Listing 8.26 and Figure 8.26).
277
Subqueries
Comparing a Subquery Value
Listing 8.25 List the books with above-average sales.

See Figure 8.25 for the result.
SELECT title_id, sales
FROM titles
WHERE sales >
(SELECT AVG(sales)
FROM titles);
Listing
title_id sales

T05 201440
T07 1500200
Figure 8.25 Result of Listing 8.25.
Listing 8.26 List the authors of the books with above-
average sales by using a join and a subquery. See
Figure 8.26 for the result.
SELECT ta.au_id, ta.title_id
FROM titles t
INNER JOIN title_authors ta
ON ta.title_id = t.title_id
WHERE sales >
(SELECT AVG(sales)
FROM titles)
ORDER BY ta.au_id ASC, ta.title_id ASC;
Listing
au_id title_id

A02 T07
A04 T05
A04 T07
Figure 8.26 Result of Listing 8.26.

Recall from the introduction to this chapter
that you can use a subquery almost any-
where an expression is allowed, so this
syntax is valid:
WHERE (subquery) op (subquery)
The left subquery must return a single value.
Listing 8.27 is equivalent to Listing 8.26,
but I’ve removed the inner join and instead
placed a correlated subquery to the left of
the comparison operator. See Figure 8.27
for the result.
You can include
GROUP BY
or
HAVING
clauses
in a subquery if you know that the
GROUP BY
or
HAVING
clause itself returns a single value.
Listing 8.28 lists the books priced higher
than the highest-priced biography. See
Figure 8.28 for the result.
278
Chapter 8
Comparing a Subquery Value
Listing 8.27 List the authors of the books with
above-average sales by using two subqueries. See
Figure 8.27 for the result.

SELECT au_id, title_id
FROM title_authors ta
WHERE
(SELECT AVG(sales)
FROM titles t
WHERE ta.title_id = t.title_id)
>
(SELECT AVG(sales)
FROM titles)
ORDER BY au_id ASC, title_id ASC;
Listing
au_id title_id

A02 T07
A04 T05
A04 T07
Figure 8.27 Result of Listing 8.27.
Listing 8.28 List the books priced higher than the
highest-priced biography. See Figure 8.28 for the
result.
SELECT title_id, price
FROM titles
WHERE price >
(SELECT MAX(price)
FROM titles
GROUP BY type
HAVING type = 'biography');
Listing
title_id price


T03 39.95
T13 29.99
Figure 8.28 Result of Listing 8.28.
Listing 8.29 uses a subquery in a
HAVING
clause to list the publishers whose average
sales exceed overall average sales. Again, the
subquery returns a single value (the average
of all sales). See Figure 8.29 for the result.
Listing 8.30 uses a correlated subquery to
list authors whose royalty share is less than
the highest royalty share of any coauthor
of a book. The outer query selects the rows of
title_authors
(that is, of
ta1
) one by one.
The subquery calculates the highest royalty
share for each book being considered for
selection in the outer query. For each possible
value of
ta1
, the DBMS evaluates the sub-
query and puts the row being considered in
the result if the royalty share is less than the
calculated maximum. See Figure 8.30 for
the result.
279
Subqueries
Comparing a Subquery Value

Listing 8.29 List the publishers whose average sales
exceed the overall average sales. See Figure 8.29 for
the result.
SELECT pub_id, AVG(sales) AS "AVG(sales)"
FROM titles
GROUP BY pub_id
HAVING AVG(sales) >
(SELECT AVG(sales)
FROM titles);
Listing
pub_id AVG(sales)

P03 506744.33
Figure 8.29 Result of Listing 8.29.
Listing 8.30 List authors whose royalty share is less
than the highest royalty share of any coauthor of a
book. See Figure 8.30 for the result.
SELECT ta1.au_id, ta1.title_id,
ta1.royalty_share
FROM title_authors ta1
WHERE ta1.royalty_share <
(SELECT MAX(ta2.royalty_share)
FROM title_authors ta2
WHERE ta1.title_id = ta2.title_id);
Listing
au_id title_id royalty_share

A04 T04 0.40
A03 T11 0.30
A04 T11 0.30

Figure 8.30 Result of Listing 8.30.

×