SELECT a1.au_fname, a1.au_lname, a2.au_fname, a2.au_lname FROM authors a1 INNER JOIN authors a2 ON a1.state = a2.state ORDER BY a1.au_id ASC, a2.au_id ASC; Listing au_fname au_lname au_f
Trang 1For every biography, Listing 7.35 lists the
other biographies that outsold it Note that
the WHEREsearch condition requires type =
‘biography’for both tables t1andt2
because the join condition considers the
columntypeto be two separate columns
See Figure 7.35 for the result.
✔ Tip
■ Using WHEREsyntax, Listing 7.35 is
equiv-alent to:
SELECT t1.title_id, t1.sales,
t2.title_id AS “Better seller”,
t2.sales AS “Higher sales”
FROM titles t1, titles t2
WHERE t1.sales < t2.sales
AND t1.type = ‘biography’
AND t2.type = ‘biography’
ORDER BY t1.title_id ASC,
t2.sales ASC;
Chapter 7
Listing 7.35 For every biography, list the title ID and
sales of the other biographies that outsold it See Figure 7.35 for the result.
SELECT t1.title_id, t1.sales, t2.title_id AS "Better seller", t2.sales AS "Higher sales"
FROM titles t1 INNER JOIN titles t2
ON t1.sales < t2.sales
WHERE t1.type = 'biography' AND t2.type = 'biography' ORDER BY t1.title_id ASC, t2.sales ASC;
Listing
title_id sales Better seller Higher sales
- - -
-T06 11320 T12 100001
T06 11320 T07 1500200
T12 100001 T07 1500200
Figure 7.35 Result of Listing 7.35.
Trang 2Listing 7.36 is a self-join to find all pairs
of authors within New York state See
Figure 7.36 for the result.
✔ Tip
■ Using WHEREsyntax, Listing 7.36 is equivalent to:
SELECT a1.au_fname, a1.au_lname, a2.au_fname, a2.au_lname FROM authors a1, authors a2 WHERE a1.state = a2.state AND a1.state = ‘NY’
ORDER BY a1.au_id ASC, a2.au_id ASC;
Listing 7.36 List all pairs of authors who live in New
York state See Figure 7.36 for the result.
SELECT
a1.au_fname, a1.au_lname,
a2.au_fname, a2.au_lname
FROM authors a1
INNER JOIN authors a2
ON a1.state = a2.state
ORDER BY a1.au_id ASC, a2.au_id ASC;
Listing
au_fname au_lname au_fname au_lname
- -
-Sarah Buchman -Sarah Buchman
Sarah Buchman Christian Kells
Christian Kells Sarah Buchman
Christian Kells Christian Kells
Figure 7.36 Result of Listing 7.36.
Trang 3Figure 7.37 still isn’t quite what I want, because the two result rows are redundant The first row states that Sarah Buchman lives in the same state as Christian Kells, and the second row gives the same informa-tion To eliminate this redundancy, I’ll change the second join condition’s comparison
oper-ator from not-equal to less-than (Listing
7.38 and Figure 7.38).
✔ Tip
■ Using WHEREsyntax, Listing 7.38 is equiv-alent to:
SELECT a1.au_fname, a1.au_lname, a2.au_fname, a2.au_lname FROM authors a1, authors a2 WHERE a1.state = a2.state AND a1.au_id < a2.au_id AND a1.state = ‘NY’
ORDER BY a1.au_id ASC, a2.au_id ASC;
The first and fourth rows of Figure 7.36
are unnecessary because they indicate that
Sarah Buchman lives in the same state as Sarah
Buchman, and likewise for Christian Kells
Adding a join condition retains only those
rows in which the two authors differ
(Listing 7.37 and Figure 7.37).
✔ Tip
■ Using WHEREsyntax, Listing 7.37 is
equiv-alent to:
SELECT
a1.au_fname, a1.au_lname,
a2.au_fname, a2.au_lname
FROM authors a1, authors a2
WHERE a1.state = a2.state
AND a1.au_id <> a2.au_id
AND a1.state = ‘NY’
ORDER BY a1.au_id ASC,
a2.au_id ASC;
Chapter 7
Listing 7.37 List all different pairs of authors who live
in New York state See Figure 7.37 for the result.
SELECT
a1.au_fname, a1.au_lname,
a2.au_fname, a2.au_lname
FROM authors a1
INNER JOIN authors a2
ON a1.state = a2.state
AND a1.au_id <> a2.au_id
WHERE a1.state = 'NY'
Listing
Listing 7.38 List all different pairs of authors who
live in New York state, with no redundancies See Figure 7.38 for the result.
SELECT a1.au_fname, a1.au_lname, a2.au_fname, a2.au_lname FROM authors a1
INNER JOIN authors a2
ON a1.state = a2.state
Listing
Trang 4To this point, I’ve used a single SELECT state-ment to retrieve data from one or more tables This chapter describes nested queries, which let you retrieve or modify data based on another query’s result
A subquery, or subselect, is a SELECT state-ment embedded in another SQL statestate-ment
You can nest a subquery in:
◆ TheSELECT,FROM,WHERE, or HAVINGclause
of a SELECTstatement
◆ Another subquery
◆ An INSERT,UPDATE, or DELETEstatement
In general, you can use a subquery anywhere
an expression is allowed, but your DBMS might restrict where they can appear This chapter covers subqueries nested in a SELECT
statement or another subquery; Chapter 10 covers subqueries embedded in INSERT,
UPDATE, and DELETEstatements
Subqueries
8
Trang 5Subqueries
This section defines some terms and
intro-duces subqueries by giving an example of a
SELECTstatement that contains a simple
sub-query Subsequent sections explain the types
of subqueries and their syntax and semantics
Suppose that you want to list the names of
the publishers of biographies The naive
approach is to write two queries: one query
to retrieve the IDs of all the biography
pub-lishers (Listing 8.1 and Figure 8.1) and a
second query that uses the first query’s result
to list the publisher names (Listing 8.2 and
Figure 8.2).
A better way is to use an inner join
(Listing 8.3 and Figure 8.3); see “Creating
an Inner Join with INNER JOIN” in Chapter 7
Another alternative is to use a subquery
(Listing 8.4 and Figure 8.4) The subquery
in Listing 8.4 is shown in red A subquery
also is called an inner query, and the
state-ment containing a subquery is called an
outer query In other words, an enclosed
sub-query is an inner sub-query of an outer sub-query
Remember that a subquery can be nested
in another subquery, so inner and outer are
relative terms in statements with multiple
nested subqueries
Chapter 8
Listing 8.1 List the biography publishers See
Figure 8.1 for the result.
SELECT pub_id FROM titles WHERE type = 'biography';
Listing
pub_id
-P01 P03 P01 P01
Figure 8.1 Result of Listing 8.1 You can add DISTINCT
to the SELECT clause of Listing 8.1 to list the publishers only once; see “Eliminating Duplicate Rows with DISTINCT ” in Chapter 4.
Listing 8.2 This query uses the result of Listing 8.1
to list the names of the biography publishers See Figure 8.2 for the result.
SELECT pub_name FROM publishers
Listing
pub_name -Abatis Publishers Schadenfreude Press
Figure 8.2 Result of Listing 8.2.
Trang 6I’ll explain how a DBMS executes subqueries
in “Simple and Correlated Subqueries” later
in this chapter, but for now, all that you need
to know is that in Listing 8.4, the DBMS processes the inner query (in red) first and then uses its interim result to run the outer query (in black) and get the final result The
INkeyword that introduces the subquery tests for list membership and works like IN
in “List Filtering with IN” in Chapter 4 Note that the inner query in Listing 8.4 is the same query as Listing 8.1, and the outer query is the same query as Listing 8.2
✔ Tips
■ Sometimes you’ll see the term subquery used to refer to an entire SQL statement
that contains one or more subqueries
To prevent confusion, I don’t use that terminology in this book
■ MySQL 4.1 and later support
subqueries, but earlier versions don’t You can’t run the examples in this chapter if you’re using MySQL 4.0 or earlier, but you have a few choices, in order of preference:
◆ Upgrade to the latest version of MySQL (www.mysql.com)
◆ Recast the subquery as a join (see
“Subqueries vs Joins” later in this chapter)
◆ Create a temporary table to hold the result of a subquery (see “Creating a Temporary Table with CREATE TEMPORARY TABLE” in Chapter 11 and the temporary-table example in the DBMS Tip in
“Creating Outer Joins with OUTER JOIN”
in Chapter 7, Listing 7.32)
◆ Simulate the subquery in a procedural host language such as PHP or Java (not covered in this book)
Listing 8.3 List the names of the biography publishers
by using an inner join See Figure 8.3 for the result.
SELECT DISTINCT pub_name
FROM publishers p
INNER JOIN titles t
ON p.pub_id = t.pub_id
WHERE t.type = 'biography';
Listing
pub_name
-Abatis Publishers
Schadenfreude Press
Figure 8.3 Result of Listing 8.3.
Listing 8.4 List the names of the biography publishers
by using a subquery See Figure 8.4 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.4 Result of Listing 8.4.
Trang 7Subquery Syntax
The syntax of a subquery is the same as
that of a normal SELECTstatement (see
Chapters 4 through 7) except for the
follow-ing differences:
◆ You can nest a subquery in a SELECT,
FROM,WHERE, or HAVINGclause or in
another subquery
◆ Always enclose a subquery in parentheses
◆ Don’t terminate a subquery with a
semi-colon (You still must terminate the
statement that contains the subquery
with a semicolon.)
◆ Don’t put an ORDER BYclause in a subquery
(A subquery returns an intermediate result
that you never see, so sorting a subquery
makes no sense.)
◆ A subquery is a single SELECTstatement
(You can’t use, say, a UNIONof multiple
SELECTstatements as a subquery.)
◆ A subquery can use columns in the
tables listed in its own FROMclause or in
the outer query’s FROMclause
◆ If a table appears in an inner query but
not in the outer query, you can’t include
that table’s columns in the final result
(that is, in the outer query’s SELECTclause)
◆ Depending on the context in which it’s
used, a subquery might be required to
return a limited number of rows or
columns The SQL standard categorizes
In practice, a subquery usually appears in a
WHEREclause that takes one of these forms:
◆ WHERE test_expr op (subquery)
◆ WHERE test_expr [NOT] IN (subquery)
◆ WHERE test_expr op ALL (subquery)
◆ WHERE test_expr op ANY (subquery)
◆ WHERE [NOT] EXISTS (subquery)
test_expr is a literal value, a column name,
an expression, or a scalar subquery; op is a
comparison operator (=,<>,<,<=,>, or >=);
and subquery is a simple or correlated
sub-query I’ll cover each of these forms later in this chapter You can use these subquery forms in a HAVINGclause, too
✔ Tip
■ The SQL standard doesn’t
specify a maximum number
of subquery nesting levels, so your DBMS will set its own upper limit This built-in limit typically exceeds the limit of human comprehension
Microsoft SQL Server, for example,
allows 32 levels of nesting
Chapter 8
Table 8.1
Size of Subquery Results
Trang 8Subqueries vs Joins
In “Understanding Subqueries” earlier in
this chapter, Listings 8.3 and 8.4 showed two equivalent queries: one used a join, and the
other used a subquery Many subqueries can
be formulated alternatively as joins In fact,
a subquery is a way to relate one table to
another without actually doing a join
Because subqueries can be hard to use and
debug, you might prefer to use joins, but you can pose some questions only as subqueries
In cases where you can use subqueries and
joins interchangeably, you should test queries
on your DBMS to see whether a
perform-ance difference exists between a statement
that uses a subquery and a semantically
equivalent version that uses a join For
example, the query
SELECT MAX(table1.col1)
FROM table1
WHERE table1.col1 IN
(SELECT table2.col1
FROM table2);
usually will run faster than
SELECT MAX(table1.col1)
FROM table1
INNER JOIN table2
ON table1.col1 = table2.col1;
For more information, see “Comparing
Equivalent Queries” later in this chapter
Trang 9The following syntax diagrams show some
equivalent statements that use subqueries
and joins These two statements are
equiva-lent (INsubquery):
SELECT *
FROM table1
WHERE id IN
(SELECT id FROM table2);
and (inner join):
SELECT DISTINCT table1.*
FROM table1
INNER JOIN table2
ON table1.id = table2.id;
See Listings 8.5a and 8.5b and Figure 8.5
for an example
Chapter 8
Listing 8.5a This statement uses a subquery to list
the authors who live in the same city in which a publisher is located See Figure 8.5 for the result.
SELECT au_id, city FROM authors WHERE city IN ( SELECT city FROM publishers );
Listing
Listing 8.5b This statement is equivalent to
Listing 8.5a but uses an inner join instead of a subquery See Figure 8.5 for the result.
SELECT DISTINCT a.au_id, a.city FROM authors a
INNER JOIN publishers p
ON a.city = p.city;
Listing
au_id city - -A03 San Francisco A04 San Francisco A05 New York
Figure 8.5 Result of Listings 8.5a and 8.5b.
Trang 10These three statements are equivalent (NOT INsubquery):
SELECT *
FROM table1 WHERE id NOT IN (SELECT id FROM table2);
and (NOT EXISTSsubquery):
SELECT *
FROM table1
WHERE NOT EXISTS (SELECT *
FROM table2 WHERE table1.id = table2.id);
and (left outer join):
SELECT table1.*
FROM table1 LEFT OUTER JOIN table2
ON table1.id = table2.id WHERE table2.id IS NULL;
See Listings 8.6a, 8.6b, and 8.6c and
Figure 8.6 for an example INandEXISTS
subqueries are covered later in this chapter
Listing 8.6a This statement uses an IN subquery to
list the authors who haven’t written (or cowritten) a
book See Figure 8.6 for the result.
SELECT au_id, au_fname, au_lname
FROM authors
(SELECT au_id FROM title_authors);
Listing
Listing 8.6b This statement is equivalent to
Listing 8.6a but uses an EXISTS subquery instead
of an IN subquery See Figure 8.6 for the result.
SELECT au_id, au_fname, au_lname
FROM authors a
(SELECT *
FROM title_authors ta
WHERE a.au_id = ta.au_id);
Listing
Listing 8.6c This statement is equivalent to
Listings 8.6a and 8.6b but uses a left outer join
instead of a subquery See Figure 8.6 for the result.
SELECT a.au_id, a.au_fname, a.au_lname
FROM authors a
LEFT OUTER JOIN title_authors ta
ON a.au_id = ta.au_id
WHERE ta.au_id IS NULL;
Listing
au_id au_fname au_lname
- -
-A07 Paddy O'Furniture
Figure 8.6 Result of Listings 8.6a, 8.6b, and 8.6c.