Students Who Attended Every Fest Workshop — SQL Medium Problem
At the college tech fest, students check in to workshops by scanning a QR code at the door.
- Difficulty: Medium
- Topics: Aggregation, Subqueries
- Dialect: MySQL
- Problem: #22
Problem statement
At the college tech fest, students check in to workshops by scanning a QR code at the door. A student who steps out and comes back scans again, so the same student and workshop can appear more than once.
Return the roll_no of every student who checked in to every workshop listed in Workshop. Return the rows in any order.
Tables
Table: Workshop
| Column | Type |
|---|---|
| workshop_id | int |
| topic | varchar |
Primary key: workshop_id.
One row per workshop at the fest.
Table: CheckIn
| Column | Type |
|---|---|
| scan_id | int |
| roll_no | int |
| workshop_id | int |
| scanned_at | datetime |
Primary key: scan_id.
One row per QR scan. Every workshop_id here is in Workshop.
Examples
Example 1
Workshop
| workshop_id | topic |
|---|---|
| 1 | Git basics |
| 2 | Intro to Docker |
| 3 | Figma for devs |
CheckIn
| scan_id | roll_no | workshop_id | scanned_at |
|---|---|---|---|
| 1 | 501 | 1 | 2025-02-14 10:02:11 |
| 2 | 501 | 2 | 2025-02-14 12:00:40 |
| 3 | 502 | 1 | 2025-02-14 10:05:03 |
| 4 | 502 | 1 | 2025-02-14 11:15:47 |
| 5 | 502 | 2 | 2025-02-14 12:01:30 |
| 6 | 501 | 3 | 2025-02-15 09:58:00 |
| 7 | 503 | 1 | 2025-02-14 10:10:10 |
| 8 | 503 | 2 | 2025-02-14 12:04:22 |
| 9 | 503 | 3 | 2025-02-15 10:01:05 |
Output
| roll_no |
|---|
| 501 |
| 503 |
How to solve Students Who Attended Every Fest Workshop
"Attended every workshop" is relational division, and the counting form is the easiest to write. Group the scans by student; the student qualifies when the number of different workshops they scanned into equals the number of workshops in the catalogue, which a scalar subquery (SELECT COUNT(*) FROM Workshop) supplies.
DISTINCT is essential. Student 502 in the example scanned three times but into only two workshops; COUNT(*) = 3 would wrongly let them through. Counting distinct ids can only match the total when every workshop is covered, because every workshop_id in CheckIn is a real workshop — without that guarantee you would have to join to Workshop first so that unknown ids could not stand in for missing ones.
The double NOT EXISTS is the textbook form: keep a student for whom there is no workshop with no matching scan. It reads like the definition and needs no counting, so duplicates never matter. The third query builds every student × workshop pair with a CROSS JOIN, attaches scans with a LEFT JOIN, and keeps students with no missing pair.
The grouped version reads CheckIn once and Workshop once; the NOT EXISTS form probes CheckIn once per student and workshop, which is fast with an index on (roll_no, workshop_id).
Reference solution (MySQL)
SELECT roll_no
FROM CheckIn
GROUP BY roll_no
HAVING COUNT(DISTINCT workshop_id) = (SELECT COUNT(*) FROM Workshop)Another way
SELECT DISTINCT c.roll_no FROM CheckIn c
WHERE NOT EXISTS (
SELECT 1 FROM Workshop w
WHERE NOT EXISTS (SELECT 1 FROM CheckIn x WHERE x.roll_no = c.roll_no AND x.workshop_id = w.workshop_id)
)Another way
SELECT s.roll_no
FROM (SELECT DISTINCT roll_no FROM CheckIn) s
CROSS JOIN Workshop w
LEFT JOIN CheckIn c ON c.roll_no = s.roll_no AND c.workshop_id = w.workshop_id
GROUP BY s.roll_no
HAVING SUM(c.scan_id IS NULL) = 0← Share of Quiz Players Who Returned the Next Day · Monthly Ticket Refund Report by Railway Zone →