Examples are based on the Travel Agency database
At Higher level, subqueries were created by saving them as SQL views, and then including the view in the FROM clause of the outer query.
However, modifying the full query would involve editing both the main query and the view separately.
Including subqueries in the WHERE clause of a query keeps everything is in one place.
Subquery in the WHERE clause of a SELECT query
A subquery is a query embedded within the WHERE clause of another SQL query. A subquery is sometimes referred to as an inner query or a nested query, and an SQL query is sometimes referred to as the outer query or the parent query.
The subquery executes before the main query, so the results can be passed to the main query as a condition to further restrict the data to be retrieved.
There are a few rules that subqueries must follow:
- Subqueries must be enclosed within brackets.
- Unless the main query has multiple fields in its SELECT clause, a subquery can have only one field in its SELECT clause.
- The BETWEEN operator can be used within a subquery but cannot be applied to the results of a subquery returned to the main query.
- Although an ORDER BY clause can be used with the main query, an ORDER BY clause cannot be used in a subquery; if it is needed, the GROUP BY clause can be used to perform the same function as the ORDER BY within a subquery.
SELECT field names FROM table names WHERE fieldName OPERATOR (SELECT field names FROM table names WHERE condition) ORDER BY field names;
Single-Value subqueries
Many subqueries return exactly one record (called single-value subqueries). The developer must check that this is the case, because an error will be generated if a subquery returns more results than expected.
-- Display details of most expensive hotel SELECT hotelName, starRating, pricePersonNight FROM Hotel WHERE pricePersonNight = (SELECT MAX(pricePersonNight) FROM Hotel );
-- Display details all hotels that have a below-average star rating. SELECT resortName, hotelName, starRating FROM Resort, Hotel WHERE Resort.resortID = Hotel.resortID AND starRating < (SELECT AVG(starRating) FROM Hotel);
/* Display details of the customer who booked the same hotel as the customer with ID 111. */ SELECT firstName, surname, postcode FROM Customer, Booking WHERE Customer.customerNo = Booking.customerNo AND NOT Customer.customerNo = 111 AND hotelRef = (SELECT hotelRef FROM Booking WHERE customerNo = 111 );
Multiple-Value subqueries
Subqueries that return more than one row (called multiple-value subqueries), can only be used with multiple-value operators such as EXISTS, IN and ANY.
IN operator
This allows multiple values to be specified as an alternative to multiple OR conditions.
/* display data on all hotels booked by customer ID 315. The subquery generate list of all hotelRefs that customer 315 has booked. The outer query looks for hotels that have a hotelref in the subquery. */ SELECT hotelName, starRating FROM Hotel WHERE hotelRef IN (SELECT hotelRef FROM Hotel, Booking WHERE Hotel.hotelRef = Booking.hotelRef AND customerNo = 315);
ANY operator
This returns true if any of the subquery values meet the condition specified in the main query.
Note: SQLite does not support the ANY operator. However you can still be required to complete design and coding questions that use this.
- WHERE fieldname > ANY (subquery)
is equivalent to greater than the minimum value in the subquery’s list - WHERE fieldname < ANY (subquery)
is equivalent to less than the maximum value in the subquery’s list
- WHERE fieldname = ANY (subquery)
is equivalent to IN subquery’s list
/*Display the customer number, hotel reference, and booking cost for any booking that costs more than any bookings made by customers with surnames Lowden, Shawfair or Sheriffhall. The subquery returns list of costs for all booking made by the three specified customers The outer query finds all bookings that cost more than any of the subquery cost */ SELECT customerNo, Hotel.hotelRef, pricePersonNight*numberNights*numberInParty AS [Booking Cost] FROM Booking, Hotel WHERE Booking.hotelRef = Hotel.hotelRef AND pricePersonNight*numberNights*numberInParty > ANY (SELECT pricePersonNight*numberNights*numberInParty FROM Booking, Hotel, Customer WHERE Booking.hotelRef = Hotel.hotelRef AND Booking.customerNo = Customer.customerNo AND surname IN ("Danderhall", "Lowden", "Shawfair") );
EXISTS
This tests for the existence of records within the subquery and returns true when the subquery returns one or more records (this is very useful to obtain records that do not meet a certain condition).
Notes:
- EXISTS only checks for presence of any records. The SELECT clause of the subquery could return any field, * is returned to keep the SQL simpler/readable.
- The subquery’s WHERE clause links to a table that is not in its own FROM clause, but to a table in outer query
/* Display details of all 3-star hotels that have bookings. */ SELECT hotelname, mealPlan, starRating, resortName FROM Hotel, Resort WHERE Hotel.resortID = Resort.resortID AND starRating = 3 AND EXISTS (SELECT * FROM Booking WHERE Booking.hotelRef = Hotel.hotelRef)
-- Display details of customers who have never made a booking. SELECT firstName, surname, address FROM Customer WHERE NOT EXISTS (SELECT * FROM Booking WHERE Customer.customerNo = Booking.customerNo);
Multiple Subqueries
Two independent subqueries:
/* Display the name, star rating, and total of nights booked for hotels that have: - a total number of customer nights booked that is more than the total number of nights booked by the customer with ID 290 (number of nights booked multiplied by number in party) - and a star rating which is less than that of the hotel with the highest star rating The query should list the hotels from lowest star rating to the highest.*/ SELECT hotelName, starRating, SUM(numberNights*numberInParty) AS [Customers Nights] FROM Hotel, Booking WHERE Hotel.hotelRef= Booking.HotelRef AND numberNights*numberInParty > ( SELECT SUM(numberNights*numberInParty) FROM Booking WHERE customerNo =290) AND starRating < (SELECT MAX(starRating) FROM Hotel) GROUP BY hotelName, starRating ORDER BY starRating;
A subquery inside a subquery:
/* Display details of the second most expensive hotels */ -- innermost subquery finds most of expensive price of all hotels -- subquery then finds largest price that is less than the most expensive price SELECT * FROM hotel WHERE pricePersonNight = ( SELECT MAX(pricePersonNight) FROM hotel WHERE pricePersonNight < ( SELECT MAX(pricePersonNight) FROM hotel ) )