SQL (PostgreSQL, MySQL, SQLite) Module 22 – SQL interview preparation
Interview drills: 'all', 'none' and division
Relational division in SQL for 'every', 'only' and 'never' questions: original interview problems, the brute force, and the NULLs that break them.
What you will learn
- Solve "applied to every drive" questions with relational division
- Solve "never" and "only" questions with anti-joins
- Compare the count, double NOT EXISTS and EXCEPT forms and pick the clearest
- Handle duplicates, NULLs and an empty set in each form
Before you start
What you’ll learn · Before you start
What you will learn
- Solve "applied to every drive" questions with relational division
- Solve "never" and "only" questions with anti-joins
- Compare the count, double NOT EXISTS and EXCEPT forms and pick the clearest
- Handle duplicates, NULLs and an empty set in each form
Before you start
On this page
Relational division finds the rows that are related to every member of a set: the students who applied to every
drive of one recruiter, the customers who bought every product of a category. SQL has no division operator, so the
answer is written in one of three ways: as a double negative (there is no drive the student did not apply to), as a
count (the student’s distinct drives equal the recruiter’s), or as a set difference with EXCEPT. Two relatives share
the interview round. “Only” means a match and nothing else, and “never” is an anti-join, the rows with no match at
all. All three are short to state and easy to get subtly wrong, because duplicate rows, rows of other groups, a NULL
in a subquery and an empty set each change the answer.
Two problems on one table of applications
Problem A: every. An invented hiring portal has students(id, name), recruiters(id, name),
drives(id, recruiter_id, role) and applications(student_id, drive_id). Return the students who applied to every
drive of Kaveri Analytics, an invented recruiter with three drives. A student may apply to the same drive twice, and
a walk-in candidate without an account has a NULL student_id. At full size there are 1 million students, 400 drives
for the largest recruiter and 30 million applications.
Problem B: only and never. Return the students who applied only to Kaveri drives, at least once and nowhere else, and the students who never applied to a Kaveri drive, including the students who applied to nothing.
"Every drive" means no drive is missing
Text description of the diagram
The diagram pairs each student with each of the three drives of Kaveri Analytics, drives 11, 12 and 13, and marks whether the student applied.
- Aarav applied to all three, and also to a Narmada Systems drive. Nothing is missing, so he is in the answer.
- Bhavna applied to 11, and to 13 twice, but not to 12, so she is out.
- Charan applied to all three, to 12 twice. Nothing is missing, so he is in the answer.
- Divya applied to 11 and 12 but not to 13, so she is out.
- Esha and Farid have no Kaveri application at all, so every pair is missing.
The answer to "applied to every Kaveri drive" is Aarav and Charan.
The brute force, and why it fails at full size
Problem A’s literal translation pairs every student with every Kaveri drive and looks each pair up:
-- An invented hiring portal: students, recruiters, their drives and the applications.
CREATE TABLE students (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO students VALUES
(1, 'Aarav'), (2, 'Bhavna'), (3, 'Charan'), (4, 'Divya'), (5, 'Esha'), (6, 'Farid');
CREATE TABLE recruiters (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO recruiters VALUES (1, 'Kaveri Analytics'), (2, 'Narmada Systems'), (3, 'Godavari Labs');
CREATE TABLE drives (id INTEGER PRIMARY KEY, recruiter_id INTEGER NOT NULL, role TEXT NOT NULL);
INSERT INTO drives VALUES
(11, 1, 'data analyst'), (12, 1, 'data engineer'), (13, 1, 'BI developer'),
(21, 2, 'backend developer'), (22, 2, 'QA engineer');
-- Godavari Labs has not announced a drive yet.
CREATE TABLE applications (
student_id INTEGER, -- NULL: a walk-in candidate without an account
drive_id INTEGER NOT NULL
);
INSERT INTO applications VALUES
(1, 11), (1, 12), (1, 13), (1, 22), -- Aarav: every Kaveri drive, and one more
(2, 11), (2, 13), (2, 13), -- Bhavna: two Kaveri drives, one of them twice
(3, 11), (3, 12), (3, 13), (3, 12), -- Charan: every Kaveri drive, one twice
(4, 11), (4, 12), (4, 21), -- Divya: two Kaveri drives and a Narmada one
(6, 21), (6, 22), -- Farid: both Narmada drives
(NULL, 12); -- a walk-in for the data engineer drive
-- Esha has not applied to anything.
-- Problem A, the literal translation: pair every student with every Kaveri drive and look
-- for each pair in applications. The student qualifies when every pair is found.
SELECT s.id, s.name
FROM students AS s
CROSS JOIN drives AS d
WHERE d.recruiter_id = 1
GROUP BY s.id, s.name
HAVING min(EXISTS (SELECT 1 FROM applications AS a
WHERE a.student_id = s.id AND a.drive_id = d.id)) = 1
ORDER BY s.id;
-- Problem B2, the first attempt: count the students whose id is not among the Kaveri applicants.
SELECT count(*) AS never_applied_to_kaveri
FROM students
WHERE id NOT IN (SELECT a.student_id
FROM applications AS a
JOIN drives AS d ON d.id = a.drive_id
WHERE d.recruiter_id = 1); Output
┌────┬────────┐ │ id │ name │ ├────┼────────┤ │ 1 │ Aarav │ │ 3 │ Charan │ └────┴────────┘ ┌─────────────────────────┐ │ never_applied_to_kaveri │ ├─────────────────────────┤ │ 0 │ └─────────────────────────┘
Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < brute_force.sql
Runs on this device, in your browser. The first run downloads SQLite (about 0.7 MB), which is kept for the next runs.
Your run, in this browser
The first result, Aarav and Charan, is right. At full size the query builds 1,000,000 × 400 = 4 × 10⁸ pairs and runs
a subquery for each, where one pass over 30 million applications would do. The second result is a first attempt at
Problem B: NOT IN against the list of Kaveri applicants. It counts 0 students, although Esha and Farid never
applied, because the walk-in’s NULL is in the list.
What to notice
- “Every” is a double negative: a student qualifies when no Kaveri drive lacks their application.
- A count works too, if it counts distinct drives and only the recruiter’s drives.
EXCEPTremoves the students who have a missing pair of student and drive.- “Only” is “has one” plus “has nothing else”, and “never” is an anti-join with
NOT EXISTS. NOT INis never true once its list holds a NULL.- Ask what an empty set means: a recruiter that has no drives yet.
Three ways to divide, on the same rows Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why the count is right only with DISTINCT and the filter, proved
- Choosing a form: the trade-offs
- A recruiter with no drives yet: the empty divisor, reproduced
- Exact division, “only” and “never”
- Paid only by one method, when some methods are unknown
- The same answers in PostgreSQL 18.6 and MySQL 8.4.11
- How the three methods scale, measured on 10,000 students
- Edge cases and common bugs
- Your turn: learners who finished every lesson
- Learners who finished every lesson of a course
- A practice set with sample answers
- Check yourself
- Interview questions
- In SQL terms, how do "applied to every drive of a recruiter", "applied only to that recruiter's drives" and "never applied to that recruiter" differ?
- A query lists the students who applied to every drive of recruiter 7 with HAVING COUNT() = (SELECT COUNT() FROM drives WHERE recruiter_id = 7). It returns one student who skipped a drive and misses another who applied to all of them. Explain both, and fix the query.
- Three queries for "students who applied to every drive of a recruiter", a count, a double NOT EXISTS and an EXCEPT, agree for every recruiter except a new one with no drives. What does each return there, which answer is right, and how do you make the query say so?
- A nightly job emails the students who never applied to any drive of a recruiter, chosen with WHERE id NOT IN (subquery). One night it sends no email at all, and nobody notices for a week. What happened, how do you confirm it quickly, and how do you make it impossible?
- Key takeaways
8 runnable examples (SQL, dialects.py, mysql_server.py, pg_server.py) · 1 exercise with sample tests · 3 interview answers · about 22 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- PostgreSQL documentation: Subquery Expressions (EXISTS, IN, NOT IN, ALL) (The PostgreSQL Global Development Group)
- PostgreSQL documentation: Combining Queries (UNION, INTERSECT, EXCEPT) (The PostgreSQL Global Development Group)
- PostgreSQL documentation: Aggregate Functions (bool_and, string_agg) (The PostgreSQL Global Development Group)
- PostgreSQL documentation: Comparison Functions and Operators (IS DISTINCT FROM) (The PostgreSQL Global Development Group)
- SQLite: The SELECT statement (compound SELECT, EXCEPT) (SQLite)
- SQLite: SQL Language Expressions (IN, NOT IN, IS NOT) (SQLite)
- SQLite: Built-in Aggregate Functions (SQLite)
- SQLite: EXPLAIN QUERY PLAN (SQLite)
- MySQL 8.4 Reference Manual: Subqueries (Oracle Corporation)
- MySQL 8.4 Reference Manual: Subqueries with ALL (Oracle Corporation)
- MySQL 8.0 Reference Manual: Set Operations with UNION, INTERSECT, and EXCEPT (Oracle Corporation)
- MySQL 8.4 Reference Manual: Comparison Functions and Operators (<=>) (Oracle Corporation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress