ASELECTthat accomplishes this is: SELECT CustomerName AS 'Customer Name' FROM Customers WHERE CustomerID IN SELECT CustomerID FROM Orders WHERE OrderType ¼ 'Cash' The resulting data is:
Trang 1In this format, theIN operator merely lists a number of values in parentheses There is also a second format for theIN, one in which an entireSELECT state-ment is inserted inside the parentheses For example, a list of states might be specified as:
WHERE State IN
(SELECT
States
FROM StateTable
WHERE Region ¼ 'Midwest')
Rather than list individual states, the second format allows us to generate a list of states through more complex logic
Let’s illustrate with an example that uses our Customers and Orders tables Let’s say we want to retrieve a list of customers who have ever paid cash for any order they’ve placed ASELECTthat accomplishes this is:
SELECT CustomerName AS 'Customer Name'
FROM Customers
WHERE CustomerID IN
(SELECT CustomerID
FROM Orders
WHERE OrderType ¼ 'Cash')
The resulting data is:
Customer Name
William Smith
Natalie Lopez
Brenda Harper is not included in the list because she has never placed an order using cash Notice that a subquerySELECTis placed entirely within the parentheses for theINkeyword Also note that the CustomerID column is used to connect the two queries Even though we are displaying CustomerName, we use CustomerID
to define the relationship between the Customers and Orders tables
Once again, we can ask whether this subquery can also be expressed as a normal query, and the answer is yes Here is an equivalent query that returns the same data:
Chapter 14 ■ Subqueries
146
Trang 2SELECT CustomerName AS 'Customer Name'
FROM Customers
INNER JOIN Orders
ON Customers.CustomerID ¼ Orders.CustomerID
WHERE OrderType ¼ 'Cash'
GROUP BY Customers.CustomerID, Customers.CustomerName
Without using a subquery, we can directly join the Customers and Orders table
However, aGROUP BYclause is now needed to ensure that we only bring back
one row per customer
Correlated Subqueries
The subqueries we’ve seen so far have been uncorrelated subqueries Generally
speaking, all subqueries can be classified as either uncorrelated or correlated.
These terms describe whether the subquery is related to the query in which it is
contained Uncorrelated subqueries are unrelated When a subquery is
unrelated, that means that it is completely independent of the outer query
Uncorrelated queries are evaluated and executed only once as part of the entire
SELECTstatement
In contrast, correlated subqueries are specifically related to the outer query
Because of this explicit relationship, correlated subqueries need to be evaluated
for each row returned and can produce different results each time the subquery is
executed
The best way to explain is with an example Returning to the Customers and
Orders tables, let’s say we want to produce a list of customers who have a total
order amount that is less than 20 dollars Here’s a statement that accomplishes
that request:
SELECT
CustomerName as 'Customer Name'
FROM Customers
WHERE
(SELECT
SUM (OrderAmount)
FROM Orders
WHERE Customers.CustomerID ¼ Orders.CustomerID)
< 20
Trang 3The result is:
Customer Name
Brenda Harper
What makes this subquery correlated, as opposed to uncorrelated? The answer can be seen by looking at the subquery itself:
SELECT
SUM (OrderAmount)
FROM Orders
WHERE Customers.CustomerID ¼ Orders.CustomerID
This subquery is correlated because it cannot be executed on its own If run by itself, this subquery would error because the Customers.CustomerID column doesn’t exist within the context of the subquery
To understand what’s going on, it’s helpful to look at the entireSELECT state-ment in a general way:
SELECT
CustomerName as 'Customer Name'
FROM Customers
WHERE
SubqueryResult < 20
As a correlated subquery, the subquery needs to be evaluated for each customer Also note that this type of subquery only works if it returns a single value Again, one might ask if this subquery could be converted into a normalSELECT
statement In this case, it can Here’s an equivalent statement that produces the same results:
SELECT
CustomerName as 'Customer Name'
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID ¼ Orders.CustomerID
GROUP BY Customers.CustomerID, CustomerName
HAVING SUM (OrderAmount) < 20
Notice that, without a subquery, the equivalent statement now requires
GROUP BY and HAVING clauses The GROUP BY clause creates groups of
Chapter 14 ■ Subqueries
148
Trang 4customers, and the HAVING clause enforces the requirement that each group
must have ordered less than 20 dollars
The EXISTS Operator
One additional technique associated with correlated subqueries is the use of a
special operator calledEXISTS This operator allows you to determine if data in
a correlated subquery exists Let’s say that you want to discover which customers
have placed orders One way of accomplishing that is with this statement:
SELECT
CustomerName AS 'Customer Name'
FROM Customers
WHERE EXISTS
(SELECT *
FROM Orders
WHERE Customers.CustomerID ¼ Orders.CustomerID)
This statement returns:
Customer Name
William Smith
Natalie Lopez
Brenda Harper
Adam Petrie doesn’t appear in the results since he has not placed any orders The
EXISTSkeyword in the above statement is evaluated as true if the SELECTin
the correlated subquery returns any data Notice that the subquery selects all
columns (SELECT *) Since it doesn’t really matter what particular data is
selected in the subquery, we use the asterisk to return all data We’re only
inter-ested in determining whether any data exists in the subquery
As before, the logic in this statement can be expressed in other ways Here’s a
statement that obtains the same results using a subquery with theINoperator:
SELECT
CustomerName AS 'Customer Name'
FROM Customers
WHERE CustomerID IN
(SELECT CustomerID
FROM Orders)
Trang 5Here’s another statement that retrieves the same data without a subquery:
SELECT
CustomerName AS 'Customer Name'
FROM Customers
INNER JOIN Orders
ON Customers.CustomerID ¼ Orders.CustomerID
GROUP BY CustomerName
Using a Subquery as a Calculated Column
The final general use of subqueries is as a calculated column Let’s say we would like to see a list of customers, along with a count of the number of orders they have placed
This query can actually be accomplished without subqueries with this statement:
SELECT
CustomerName AS 'Customer Name',
COUNT (OrderID) AS 'Number of Orders'
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID ¼ Orders.CustomerID
GROUP BY Customers.CustomerID, CustomerName
ORDER BY Customers.CustomerID
The output is:
Customer Name Number of Orders
William Smith 1
Natalie Lopez 3
Brenda Harper 2
Adam Petrie 0
However, another way of obtaining the same result is to use a subquery as a calculated column This looks like the following:
SELECT
CustomerName AS 'Customer Name',
(SELECT
COUNT (OrderID)
FROM Orders
Chapter 14 ■ Subqueries
150