Sub Queries
Sub Queries
Subqueries are not limited to DML—they can also be used in a CREATE TABLE or CREATE
VIEW statement as well.
Types of Subqueries
Single-row subqueries
Multiple-row subqueries
Multiple-column subqueries
Correlated subqueries (can exist in SELECT, UPDATE, DELETE)
Scalar subqueries
To prevent errors such as above, use where condition on primary key or unique, use aggregated
function without group by or ROWNUM < 2.
01 SELECT EMPLOYEE_ID
02 FROM EMPLOYEES
03 WHERE (FIRST_NAME, LAST_NAME) IN
04 (SELECT FIRST_NAME, LAST_NAME
05 FROM CRUISE_CUSTOMERS)
06 AND SHIP_ID = 1;
The datatypes of the columns must match—meaning that the columns on line 3 must match the
datatypes of the columns identified on line 4 or to be comparable datatypes such that automatic
datatype conversion is capable of making them the same datatypes.
SELECT VENDOR_NAME,
(SELECT TERMS_OF_DISCOUNT FROM INVOICES WHERE INVOICE_ID = 1) AS DISCOUNT
FROM VENDORS
ORDER BY VENDOR_NAME;
Scalar subqueries must always be enclosed in parentheses.
Scalar subquery expressions cannot be used in the following locations:
In CHECK constraints
In GROUP BY clauses
In HAVING clauses
In a function-based index (which is coming up in Chapter 11)
As a DEFAULT value for a column
In the RETURNING clause of any DML statement
In the WHEN conditions of CASE
In the START WITH and CONNECT BY clauses
Other than that, they can be used anywhere you would use an expression.
Correlated Subqueries
Correlated subqueries include references to elements of a parent query, and thus, they do not
exist as standalone queries, as do the examples we’ve seen so far.
01 SELECT A.SHIP_CABIN_ID, A.ROOM_STYLE, A.ROOM_NUMBER, A.SQ_FT
02 FROM SHIP_CABINS A
03 WHERE A.SQ_FT > (SELECT AVG(SQ_FT)
04 FROM SHIP_CABINS
05 WHERE ROOM_STYLE = A.ROOM_STYLE)
06 ORDER BY A.ROOM_NUMBER;
Correlated subqueries can exist in SELECT, UPDATE, and DELETE statements. A table alias is
not necessarily required in the subquery if no column name conflict exists. It’s important to note
that correlated subqueries may introduce performance degradation into a query.
01 UPDATE PORTS P
02 SET CAPACITY = (SELECT COUNT(*)
03 FROM SHIPS
04 WHERE HOME_PORT_ID = P.PORT_ID)
05 WHERE EXISTS (SELECT *
06 FROM SHIPS
07 WHERE HOME_PORT_ID = P.PORT_ID);
The EXISTS keyword tests for the existence of any rows in a subquery. If no rows are found, the
answer is FALSE. Otherwise, the subquery returns TRUE. NOT EXISTS reverses the results.
01 SELECT PORT_ID, PORT_NAME
02 FROM PORTS P1
03 WHERE EXISTS (SELECT *
04 FROM SHIPS S1
05 WHERE P1.PORT_ID = S1.HOME_PORT_ID);
The entire subquery is executed, even though EXISTS need only know whether or not the
subquery returns any rows—so beware using EXISTS with subqueries that return large numbers
of rows.
It’s worth noting that this sort of query is sometimes referred to as a “semijoin”. A semijoin is a
SELECT statement that uses the EXISTS keyword to compare rows in a table with rows in
another table.
You can use the keyword WITH to assign a name to a subquery block. Once the name is
assigned, you can reference the name from elsewhere in the query. WITH is considered a clause
of the SELECT statement.
01 WITH
02 PORT_BOOKINGS AS (
03 SELECT P.PORT_ID, P.PORT_NAME, COUNT(S.SHIP_ID) CT
04 FROM PORTS P, SHIPS S
05 WHERE P.PORT_ID = S.HOME_PORT_ID
06 GROUP BY P.PORT_ID, P.PORT_NAME
07 ),
08 DENSEST_PORT AS (
09 SELECT MAX MAX_CT
10 FROM PORT_BOOKINGS
11 )
12 SELECT PORT_NAME
13 FROM PORT_BOOKINGS
14 WHERE CT = (SELECT MAX_CT FROM DENSEST_PORT);
The series of one or more subquery blocks defined before the SELECT statement is referred to as
the subquery factoring clause. WITH can define one subquery factoring clause; it must be
defined before the SELECT statement. Internally, Oracle SQL treats a named query within the
WITH clause as a temporary table or as an inline view. If you use WITH to name a subquery,
that name isn’t recognized within the subquery itself but is recognized in almost every other
location in the overall query.