Quiz 2

07 - Advanced SQL: Subqueries, Set Operations & Views

2062 words
10 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# 07 - Advanced SQL: Subqueries, Set Operations & Views ## 🎯 Learning Objectives After reading this topic, you will be able to: - Write nested subqueries using IN, ANY, ALL, EXISTS, UNIQUE - Use set operations UNION, INTERSECT, and EXCEPT (with/without ALL) - Create and use views (virtual tables) - Write WITH (CTE)...

07 - Advanced SQL: Subqueries, Set Operations & Views

🎯 Learning Objectives

After reading this topic, you will be able to:
  • Write nested subqueries using IN, ANY, ALL, EXISTS, UNIQUE
  • Use set operations UNION, INTERSECT, and EXCEPT (with/without ALL)
  • Create and use views (virtual tables)
  • Write WITH (CTE) queries for temporary view definitions

📋 Prerequisites

📖 Core Content

7.1 Intuition: Queries Within Queries

Sometimes you need the result of one query to answer another:
  • "Find instructors whose salary is greater than the average" — you need the average first
  • "Find departments that have at least one instructor earning > 90000"
  • "Find courses that every student has taken" Subqueries (inner queries nested inside outer queries) solve these problems elegantly.
Why This Matters: Subqueries let you express complex conditions that would be impossible with a single flat SELECT. They're essential for real-world analytics.

7.2 Subqueries with IN

The IN operator checks if a value is in a set returned by a subquery:
sql
-- Find instructors who teach in departments with budget > 100000
SELECT name
FROM instructor
WHERE dept_name IN (
    SELECT dept_name
    FROM department
    WHERE budget > 100000
);
Execution: The inner query runs first, returning departments with budget > 100000. The outer query then finds instructors in those departments.

NOT IN

sql
-- Find instructors NOT in departments with budget > 100000
SELECT name
FROM instructor
WHERE dept_name NOT IN (
    SELECT dept_name
    FROM department
    WHERE budget > 100000
);
Warning: If the subquery result contains NULL, NOT IN returns no rows! (Because X NOT IN (NULL, 'CS', 'Math') evaluates to UNKNOWN — NULL comparison always yields UNKNOWN.)

7.3 Subqueries with ANY and ALL

ANY (some)

F <comp> ANY r is TRUE if the comparison holds for at least one tuple in r.
sql
-- Find instructors whose salary is greater than SOME instructor in Biology
SELECT name
FROM instructor
WHERE salary > SOME (
    SELECT salary
    FROM instructor
    WHERE dept_name = 'Biology'
);
Key equivalences:
  • = SOMEIN
  • <> SOMENOT IN (important distinction!)

ALL

F <comp> ALL r is TRUE if the comparison holds for every tuple in r.
sql
-- Find the instructor(s) with the highest salary
SELECT name
FROM instructor
WHERE salary >= ALL (
    SELECT salary
    FROM instructor
);
Key equivalences:
  • <> ALLNOT IN
  • = ALLIN (unless the subquery has only one distinct value)

7.4 Subqueries with EXISTS

EXISTS r returns TRUE if r is non-empty.
sql
-- Find departments that have at least one instructor
SELECT dept_name
FROM department D
WHERE EXISTS (
    SELECT *
    FROM instructor I
    WHERE I.dept_name = D.dept_name
);
Correlated subquery: The inner query references the outer query's table (D.dept_name). For each department, the inner query checks if there's at least one matching instructor.

NOT EXISTS

sql
-- Find departments with NO instructors
SELECT dept_name
FROM department D
WHERE NOT EXISTS (
    SELECT *
    FROM instructor I
    WHERE I.dept_name = D.dept_name
);

EXISTS for Relational Division

"Find students who have taken ALL courses offered by the CS department":
sql
SELECT S.ID, S.name
FROM student S
WHERE NOT EXISTS (
    -- Courses in CS that this student has NOT taken
    (SELECT course_id FROM course WHERE dept_name = 'Comp. Sci.')
    EXCEPT
    (SELECT course_id FROM takes WHERE takes.ID = S.ID)
);
This is the SQL version of relational division (÷) — one of the most powerful query patterns.

7.5 Subqueries with UNIQUE

UNIQUE r returns TRUE if r has no duplicate tuples:
sql
-- Find courses that have only one section (no duplicates in offering)
SELECT course_id
FROM course C
WHERE UNIQUE (
    SELECT course_id
    FROM section S
    WHERE S.course_id = C.course_id
);

7.6 Set Operations: UNION, INTERSECT, EXCEPT

Set operations combine results from two queries. Both queries must have the same number of columns with compatible data types. (Diagram)

UNION

Rows that appear in either query result:
sql
-- Courses taught in Fall 2017 or Spring 2018
(SELECT course_id FROM teaches WHERE semester = 'Fall' AND year = 2017)
UNION
(SELECT course_id FROM teaches WHERE semester = 'Spring' AND year = 2018);

INTERSECT

Rows that appear in both query results:
sql
-- Courses taught in both Fall 2017 AND Spring 2018
(SELECT course_id FROM teaches WHERE semester = 'Fall' AND year = 2017)
INTERSECT
(SELECT course_id FROM teaches WHERE semester = 'Spring' AND year = 2018);

EXCEPT (also called MINUS)

Rows that appear in the first query but NOT the second:
sql
-- Courses taught in Fall 2017 but NOT in Spring 2018
(SELECT course_id FROM teaches WHERE semester = 'Fall' AND year = 2017)
EXCEPT
(SELECT course_id FROM teaches WHERE semester = 'Spring' AND year = 2018);

ALL Variants

By default, set operations remove duplicates. To preserve duplicates:
OperationDuplicate Count
UNION ALLm + n duplicates
INTERSECT ALLmin(m, n) duplicates
EXCEPT ALLmax(0, m - n) duplicates
Where m = count in first result, n = count in second result.

7.7 WITH Clause (CTE — Common Table Expression)

The WITH clause defines a temporary view that exists only for the duration of the query:
sql
-- Find all departments with above-average budget
WITH dept_avg AS (
    SELECT AVG(budget) AS avg_budget FROM department
)
SELECT dept_name, budget
FROM department, dept_avg
WHERE department.budget > dept_avg.avg_budget;

Multiple CTEs

sql
-- Find the department with the maximum total salary
WITH dept_total(dept_name, total_salary) AS (
    SELECT dept_name, SUM(salary)
    FROM instructor
    GROUP BY dept_name
),
max_total(max_salary) AS (
    SELECT MAX(total_salary) FROM dept_total
)
SELECT dept_name, total_salary
FROM dept_total, max_total
WHERE dept_total.total_salary = max_total.max_salary;

7.8 Views

A view is a virtual table — it doesn't store data, just a query definition.
sql
CREATE VIEW faculty AS
SELECT ID, name, dept_name
FROM instructor;
Now you can query the view like a regular table:
sql
SELECT * FROM faculty WHERE dept_name = 'Comp. Sci.';
Why views matter:
PurposeExample
SecurityHide salary column from certain users
SimplicityProvide a simplified interface to complex queries
ConsistencyChanges to underlying tables automatically reflected

Materialized Views

A materialized view stores the result physically:
sql
CREATE MATERIALIZED VIEW dept_salary_stats AS
SELECT dept_name, COUNT(*), AVG(salary)
FROM instructor
GROUP BY dept_name;
  • Pros: Fast access (no recomputation needed)
  • Cons: Must be refreshed when underlying data changes

7.9 Worked Examples

Example 1: Find instructors with salary > average (correlated)

sql
SELECT name, salary
FROM instructor I
WHERE salary > (
    SELECT AVG(salary)
    FROM instructor
    WHERE dept_name = I.dept_name  -- correlated: same department
);
This finds instructors earning above the average for their OWN department.

Example 2: Using WITH for readability

Without WITH:
sql
SELECT dept_name
FROM instructor
WHERE salary > (SELECT AVG(salary) FROM instructor)
ORDER BY dept_name;
With WITH:
sql
WITH overall_avg AS (
    SELECT AVG(salary) AS avg_sal FROM instructor
)
SELECT dept_name
FROM instructor, overall_avg
WHERE salary > overall_avg.avg_sal
ORDER BY dept_name;

📐 Key Formulas / Concepts

OperatorMeaningKey Equivalence
INValue in set= SOME
NOT INValue not in set<> ALL
ANY/SOMEComparison with any> ANY = greater than smallest
ALLComparison with all> ALL = greater than largest
EXISTSSet is non-emptyEXISTS rr ≠ ∅
UNIQUENo duplicatesUNIQUE rno dups in r

⚠️ Common Pitfalls

Pitfall 1: NOT IN with NULL

The Mistake: WHERE dept_name NOT IN (SELECT dept_name FROM department WHERE budget < 50000) — if any department has NULL budget, this returns no rows! Why: X NOT IN (NULL, 'CS', 'Math') is equivalent to X <> NULL AND X <> 'CS' AND X <> 'Math'. X <> NULL is UNKNOWN, so the whole AND becomes UNKNOWN or FALSE. Fix: Use NOT EXISTS instead, or filter NULLs explicitly.

Pitfall 2: Confusing = ANY with = ALL

The Mistake: Using = ALL when you mean = ANY. Why: = ANY means "equal to at least one" (like IN). = ALL means "equal to every one" — which is almost never what you want unless all values are identical. Fix: Think: ANY = "at least one", ALL = "for every one".

Pitfall 3: Forgetting UNION removes duplicates

The Mistake: Using UNION when you need ALL results including duplicates. Why: UNION adds the cost of duplicate elimination. If you know there are no duplicates, or if duplicates are acceptable, use UNION ALL (which is faster). Fix: Use UNION ALL when duplicates are fine and performance matters.

📝 Practice Questions

Q1. Write a query using EXISTS to find departments that have at least one instructor.

Answer
sql
SELECT dept_name
FROM department D
WHERE EXISTS (
    SELECT 1 FROM instructor I WHERE I.dept_name = D.dept_name
);
SELECT 1 is an optimization — we don't care about the actual data, just existence.

Q2. Find all courses taught in Fall 2017 but not in Spring 2018, using EXCEPT.

Answer
sql
(SELECT course_id FROM teaches WHERE semester = 'Fall' AND year = 2017)
EXCEPT
(SELECT course_id FROM teaches WHERE semester = 'Spring' AND year = 2018);

Q3. What is the difference between UNION and UNION ALL?

Answer
  • UNION: Removes duplicate tuples from the result (slower, but cleaner)
  • UNION ALL: Preserves all duplicates (faster)
If a tuple appears m times in the first query and n times in the second:
  • UNION: appears once
  • UNION ALL: appears m + n times

Q4. Why does NOT IN fail with NULL values? How do you fix it?

Answer
X NOT IN (NULL, 'A', 'B') is equivalent to X <> NULL AND X <> 'A' AND X <> 'B'. Since X <> NULL is always UNKNOWN (regardless of X), the entire expression becomes UNKNOWN, and the WHERE clause treats it as FALSE.
Fix: Use NOT EXISTS:
sql
SELECT name FROM instructor I
WHERE NOT EXISTS (
    SELECT 1 FROM department D
    WHERE D.dept_name = I.dept_name AND D.budget < 50000
);

Q5. Write a query using WITH to find departments with above-average number of instructors.

Answer
sql
WITH dept_count AS (
    SELECT dept_name, COUNT(*) AS num_instructors
    FROM instructor
    GROUP BY dept_name
),
avg_count AS (
    SELECT AVG(num_instructors) AS avg_instructors FROM dept_count
)
SELECT dept_name, num_instructors
FROM dept_count, avg_count
WHERE dept_count.num_instructors > avg_count.avg_instructors;

Q6. What is a view in SQL? When would you use one?

Answer
A view is a virtual table defined by a query. It stores the query definition, not the data.
Use when:
  • Security: Hide sensitive columns (e.g., salary) from certain users
  • Simplicity: Provide a simplified interface to complex joins
  • Consistency: Ensure everyone uses the same query logic

Q7. Write a query to find students who have taken ALL courses offered by the CS department.

Answer
sql
SELECT S.ID, S.name
FROM student S
WHERE NOT EXISTS (
    (SELECT course_id FROM course WHERE dept_name = 'Comp. Sci.')
    EXCEPT
    (SELECT T.course_id FROM takes T WHERE T.ID = S.ID)
);
This uses double negation: find students for whom there does NOT exist a CS course they have NOT taken.

Q8. What is the execution order of a query with subqueries?

Answer
For uncorrelated subqueries: The inner query runs first, produces a result, and the outer query uses that result.
For correlated subqueries: For each tuple in the outer query, the inner query is re-evaluated using the current outer tuple's values. This is like a nested loop.
Correlated subqueries can be slower because they run the inner query many times.

🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.