A subquery is a SELECT
- FROM
- WHERE
expression that is nested within another query.
SQL provides a mechanism for the nesting of subqueries.
Set membership
SQL allows testing tuples for membership in a relation, using IN
or NOT IN
clause.
Examples
- Name all instructors whose name is neither “Mozart” nor “Einstein”
SELECT DISTINCT name
FROM instructor
WHERE name NOT IN ('Mozart', 'Einsten');
- Find all the courses taught in the Fall 2009 semester, but not in the Spring 2010 semester
SELECT DISTINCT course_id
FROM section
WHERE semester = 'Fall' AND year = 2009 AND course_id NOT IN
(
SELECT course_id
FROM section
WHERE semester = 'Spring' AND year = 2010
);
- Find the total number of distinct students who have taken course sections taught by the instructor with ID
10101
- As seen in the example query below, it is possible to test for membership in an arbitrary relation.
SELECT COUNT(DISTINCT ID)
FROM takes
WHERE (course_id, sec_id, semester, year) IN
(
SELECT course_id, sec_id, semester, year
FROM teaches
WHERE teaches.ID = 10101
);
Set comparison
SOME
clause
SOME
clause can check if the predicate is satisfied by at least one tuple in the relation.
F <comp> SOME r
such that , where= SOME
IN
, however<> SOME
NOT IN
Example
Find names of instructors with salary greater than that of some (at least one) instructor in the Biology department
SELECT DISTINCT T.name
FROM instructor AS T,
instructor AS S
WHERE T.salary > S.salary AND S.dept_name = 'Biology';
-- Using SOME clause
SELECT name
FROM instructor
WHERE salary > SOME
(
SELECT salary
FROM instructor
WHERE dept_name = 'Biology'
);
ALL
clause
ALL
clause can check if the predicate is satisfied by all of the tuples in the relation.
F <comp> ALL r
such that , where<> ALL
NOT IN
, however= ALL
IN
Example
Find the names of all instructors whose salary is greater the salary of all instructors in the Biology department
SELECT name
FROM instructor
WHERE salary > ALL
(
SELECT salary
FROM instructor
WHERE dept_name = 'Biology'
);
Test for empty relations
The EXISTS
construct returns the value TRUE
if the argument subquery is nonempty.
EXISTS r
NOT EXISTS r
Example
- Find all students who have taken all courses offered in the Biology department
SELECT DISTINCT S.ID,
S.name
FROM student AS S
WHERE NOT EXISTS
(
SELECT course_id
FROM course
WHERE dept_name = 'Biology'
EXCEPT
SELECT T.course_id
FROM takes AS T
WHERE S.ID = T.ID
);
- Note that
- This query cannot be written using
= ALL
and its variants
Test for the absence of duplicate tuples
The UNIQUE
construct tests whether a subquery has any duplicate tuples in its result.
It evaluates to TRUE
if a given subquery contains no duplicates.
Example
- Find all courses that were offered at most once in 2017
SELECT T.course_id
FROM course AS T
WHERE UNIQUE
(
SELECT R.course_id
FROM course AS R
WHERE T.course_id = R.course_id AND
R.year = 2017
);
Subqueries in the FROM
clause
Since any SELECT
- FROM
- WHERE
clause returns a relation as a result, it can be inserted into another SELECT
- FROM
- WHERE
aanywhere that a relation can appear.
Example
Find the average instructors’ salaries of those departments where the average salary is greater than 42000
SELECT dept_name,
avg_salary
FROM
(
SELECT dept_name,
AVG(salary) AS avg_salary
FROM instructor
GROUP BY dept_name
)
WHERE avg_salary > 42000;
- Since the subquery in the
FROM
clause computes the average salary,HAVING
query is not required; the predicate is rather inside theWHERE
clause of the outer query. - The above query is equivalent to
SELECT dept_name,
AVG(salary) AS avg_salary
FROM instructor
GROUP BY dept_name
HAVING avg_salary > 42000
WITH
clause
The WITH
clause provides a way of defining a temporary relation whose definition is available only to the query in which the WITH
clause occurs.
Examples
- Find all departments with the maximum budget
WITH max_budget(value) AS
(
SELECT MAX(budget)
FROM department
)
SELECT department.dept_name
FROM department,
max_budget
WHERE department.budget = max_budget.value
- Find all departments where the total salary is greater than the average of the total salary at all departments
WITH dept_total(dept_name, value) AS
(
SELECT dept_name,
SUM(salary)
FROM departments
GROUP BY dept_name
),
dept_total_avg(value) AS
(
SELECT AVG(value)
FROM dept_total
)
SELECT dept_name
FROM dept_total,
dept_total_avg
WHERE dept_total.value > dept_total_avg.value;
Scalar subquery
Scalar subqeury is used where a single value is expected. It incurs a runtime error if the subquery returns more than one result tuple.
Example
- Find all departments, along with the number of instructors, in each department
SELECT dept_name,
(
SELECT COUNT(*)
FROM instructor
WHERE department.dept_name = instructor.dept_name
GROUP BY dept_name
) AS num_instructors
FROM department;