10 Subqueries
10.1 Module Introduction
This module introduces Oracle SQL subqueries, a foundational topic in advanced SQL reporting. Students will learn how to embed subqueries within main queries to achieve powerful, expressive logic in their SQL. We will begin with scalar and nested subqueries, then explore correlated subqueries using EXISTS
, ANY
, and ALL
, and conclude with how subqueries can be embedded in SELECT
lists and CASE
expressions.
Reference: Chapter 8, Labs 8.1–8.4
10.2 Explanation
10.2.1 Scalar and Nested Subqueries
A scalar subquery returns a single value and can be used wherever a single value is expected.
Example:
SELECT first_name, last_name,
SELECT MAX(cost) FROM course) AS max_course_cost
(FROM student
WHERE ROWNUM <= 3;
This query adds the maximum course cost as a column to each student record, limiting to 3 rows for display.
A nested subquery is placed inside another subquery or the main query. It can return a set of values used by the outer query.
Example:
SELECT first_name, last_name
FROM student
WHERE student_id IN (
SELECT student_id
FROM enrollment
WHERE section_id IN (
SELECT section_id
FROM section
WHERE location = 'Main Campus'
) );
This query finds students enrolled in sections at the Main Campus using nested subqueries.
Reference: Lab 8.1
10.2.3 Subqueries in SELECT and CASE
Scalar subqueries can be placed inside a SELECT
clause to compute derived columns.
Example:
SELECT student_id,
SELECT COUNT(*) FROM enrollment e WHERE e.student_id = s.student_id) AS total_courses
(FROM student s;
This query counts the number of courses each student is enrolled in.
Subqueries in CASE
can be used for conditional logic.
Example:
SELECT student_id,
CASE
WHEN (SELECT COUNT(*) FROM enrollment e WHERE e.student_id = s.student_id) > 2 THEN 'Active'
ELSE 'Low Enrollment'
END AS enrollment_status
FROM student s;
This query assigns an “Active” status to students enrolled in more than 2 courses, otherwise “Low Enrollment”.
10.2.4 A Word of Caution
Subqueries can be very powerful, but try to not overuse them.
- It can be tempting to try to accomplish everything with subqueries, but doing so can come at heavy performance and readability costs.
- One or two subqueries are fine. However, having three or more nested subqueries is usually a sign that something might be wrong. Take a step back and see if you can write your query some other way.
- We will talk about alternative ways to accomplish the same tasks without overusing subqueries in a later module.
Reference: Lab 8.4
10.3 Exercises
1. Find all students from the student
table whose zip
code is the same as that of instructor ID 101 in the instructor
table.
2. List all courses from the course
table that have a cost
greater than the average course cost.
3. Display each student’s first_name
and last_name
from the student
table and the number of courses they are enrolled in using the enrollment
table.
4. List course_no
and cost
from the course
table for all courses that are more expensive than every course that has a non-null prerequisite
value.
5. Show students from the student
table who have received a grade in the grade
table in a section (from the section
table) taught by an instructor (from the instructor
table) who lives in the same zip
code.
6. Display each student_id
from the student
table and show “Active” if enrolled in 2 or more sections (from the enrollment
table), else “Inactive”, using a subquery inside a CASE
expression.
10.4 Q&A
Some food for thought:
- How does a correlated subquery differ in performance from a standard subquery?
- When would you choose
EXISTS
overIN
? - What are the benefits and drawbacks of using subqueries vs. joins?
- What are the performance implications of using subqueries in the SELECT clause?
- How do you decide between using ANY vs ALL in subquery comparisons?
- When might a subquery be more readable than a complex join?
10.5 Additional Resources
- Oracle SQL by Example, Chapter 8, Labs 8.1–8.4
- Oracle SQL Language Reference: Subqueries
- Oracle LiveSQL Subquery Examples
10.6 Answers
1.
SELECT *
FROM student
WHERE zip = (
SELECT zip
FROM instructor
WHERE instructor_id = 101
);
2.
SELECT *
FROM course
WHERE cost > (
SELECT AVG(cost)
FROM course
);
3.
SELECT s.first_name, s.last_name,
SELECT COUNT(*)
(FROM enrollment e
WHERE e.student_id = s.student_id) AS course_count
FROM student s;
4.
SELECT course_no, cost
FROM course
WHERE cost > ALL (
SELECT cost
FROM course
WHERE prerequisite IS NOT NULL
);
5.
SELECT DISTINCT s.first_name, s.last_name
FROM student s
JOIN grade g ON s.student_id = g.student_id
JOIN section sec ON g.section_id = sec.section_id
WHERE EXISTS (
SELECT 1
FROM instructor i
WHERE i.instructor_id = sec.instructor_id
AND i.zip = s.zip
);
6.
SELECT s.student_id,
CASE
WHEN (SELECT COUNT(*) FROM enrollment e WHERE e.student_id = s.student_id) >= 2 THEN 'Active'
ELSE 'Inactive'
END AS status
FROM student s;