Relational Database Systems Concepts, Design, SQL, PostgreSQL, MySQL, and Applications

Appendices

Appendix K. SQL Practice Problems and Solutions

Fifteen problems against the canonical university database (Appendix H), rising difficulty, each with its solution and — where deterministic — the expected output. Predict before running; the practice is the prediction.

Set 1 — Single table (Chapters 10–11)

K1. Students admitted in 2022 or later whose GPA is at least 3.5, ordered by GPA descending. Solution: SELECT full_name, admission_year, gpa FROM student WHERE admission_year >= 2022 AND gpa >= 3.5 ORDER BY gpa DESC; Output: two rows — Arif Mahmud (2023, 3.90), then Tanvir Alam (2022, 3.55).

K2. The number of students, the number with a GPA, and the average GPA rounded to two places. Solution: SELECT COUNT(*) AS students, COUNT(gpa) AS with_gpa, ROUND(AVG(gpa), 2) AS avg_gpa FROM student; Output: 12, 11, 3.48.

K3. Each department's student count and average GPA, largest count first. Solution: SELECT major_dept_id, COUNT(*) AS students, ROUND(AVG(gpa),2) AS avg_gpa FROM student GROUP BY major_dept_id ORDER BY students DESC, major_dept_id; Output: 1 (4, 3.66), 2 (3, 3.61), 3 (2, 3.10), 4 (2, 2.98), 5 (1, 3.60).

Set 2 — Joins and subqueries (Chapters 12, 3)

K4. Every section of Database Systems (CSE221) with its instructor, room, and enrollment count — including sections with zero enrollments. Solution: LEFT JOIN course_section to enrollment, filter by course_id. Output: section 3 (Farhana Rahman, SAC-401, 2), section 12 (Farhana Rahman, SAC-401, 2) — CSE221 has no empty section in the data; adjust the problem to CSE321 to see the zero row (section 5, 0).

K5. Students enrolled in both section 11 and section 12 (both in progress). Solution: intersect the two enrollment sets (or a self-join). Output: Shahriar Islam (21500002).

K6. The division question: students enrolled in every section taught by instructor 101 (sections 1, 4, 13). Solution: double NOT EXISTS (Chapter 3.9/12.8). Output: the empty set — no student is in all three.

K7. Each student's total grade points (sum of grade_points over graded enrollments, 0 for none), with the student's name, highest total first. Solution: LEFT JOIN enrollment, filter graded, GROUP BY, with the grade-point mapping (Chapter 11's CASE or Chapter 22's function). Output: Nusrat 11.0 (4.0+3.7+3.3), Rakib 10.3 (3.3+3.0+4.0), Sadia 7.7 (4.0+3.7), Tanvir 10.3 (3.7+3.3+4.0), Mehjabin 7.0 (4.0+3.7+3.0), Farhan 6.3 (2.3+4.0), Imran 3.0, Sumaiya 3.0, Nabil 3.3, Arif 4.0, Shahriar 0, Zara 0.

Set 3 — Aggregates, windows, sets (Chapters 11, 13)

K8. The grade distribution with each grade's percentage of the 20 graded enrollments. Solution: GROUP BY grade with 100.0 * COUNT(*) / 20, or the window form 100.0 * COUNT(*) / SUM(COUNT(*)) OVER (). Output: A 35.0%, A- 20.0%, B+ 20.0%, B 20.0%, C+ 5.0%.

K9. A running total of enrollments by section (chronological by year, semester, section). Solution: counts per section joined to course_section, SUM(COUNT(*)) OVER (ORDER BY ...). Output: 28 rows; the total reaches 28 (section 5 contributes 0).

K10. The top student by GPA per admission cohort (year). Solution: ROW_NUMBER() OVER (PARTITION BY admission_year ORDER BY gpa DESC NULLS LAST), filter rn = 1. Output: 2021 Sadia Afrin (3.88), 2022 Mehjabin Chowdhury (3.70), 2023 Arif Mahmud (3.90), 2025 Shahriar Islam (3.25), 2026 Zara Hossain (NULL — state the NULL-handling choice).

K11. Enrollments per year pivoted by semester (Fall / Spring / Summer columns). Solution: the conditional-aggregation pivot (Chapter 13.7). Output: 2024 (11, 0, 0), 2025 (0, 9, 0), 2026 (8, 0, 0).

Set 4 — Design and DDL (Chapters 9, 6, 22)

K12. Write the DDL for waitlist(student_id, section_id, position, entered_at) — one active wait per student per section, positions unique within a section, and a defensible ON DELETE choice per foreign key. Solution: PK (student_id, section_id); UNIQUE (section_id, position); FK student CASCADE (a waitlist entry dies with its student), FK section CASCADE (the wait dies with the section) — ownership lines (Chapter 6.7); position SMALLINT CHECK (position >= 1).

K13. A stored procedure post_grade(student, section, grade) that refuses re-grading (a posted grade may only change through the audit path) — with the audit trigger of Chapter 22.5 attached. Solution: SELECT ... FOR UPDATE the enrollment; if grade IS NOT NULL, SIGNAL/RAISE 'already graded'; else UPDATE (the audit trigger records NULL → grade). Test: posting Arif's section-12 NULL grade to 'A' succeeds and audits; re-posting fails with the custom signal.

K14. Why does WHERE gpa <> NULL return no rows, and give both correct forms? Solution: comparison with NULL is UNKNOWN, and WHERE keeps only TRUE; correct: WHERE gpa IS NOT NULL or WHERE gpa IS DISTINCT FROM NULL (Chapter 10.8).

Set 5 — Optimization (Chapter 19)

K15. On the 100,000-row enrollment_history (Chapter 19.12), make "students who enrolled in section 8 in each month of 2025" index-served; show the before and after plans. Solution: a composite index on (section_id, recorded_at) — equality left, range right; the plan moves from Seq/ALL to Bitmap/Range on (section_id) with a condition on recorded_at; estimates match after ANALYZE. The write-side note: the index earns its keep only if this query is hot — the Chapter 19 judgment.