Practice SQL
You’ve read the lessons — now prove it. Take the quiz for instant feedback, then work the coding exercises. Try each one yourself before you reveal the solution.
Quick quiz
18 questions- 1
What does
SELECT DISTINCT city FROM students;return? - 2
Which clause filters rows after aggregation?
- 3
Given
students(id, name, level, city), what does this return?SELECT COUNT(*) FROM students WHERE city = 'London'; - 4
Which SQL clause limits the number of rows returned?
- 5
What does an
INNER JOINbetweenstudentsandenrolmentsonstudent_idreturn? - 6
What does
LEFT JOINguarantee thatINNER JOINdoes not? - 7
Which
WHEREcondition finds students whose name starts with the letter M? - 8
What does
SELECT AVG(level) FROM students;return when thelevelcolumn has someNULLvalues? - 9
Which statement correctly inserts a new row?
- 10
What is the purpose of a primary key constraint?
- 11
Which query returns students not enrolled in any course (assuming
enrolments.student_idreferencesstudents.id)? - 12
What does adding an index on
students(city)primarily improve? - 13
What does
ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY score DESC)do? - 14
In a CTE (WITH clause), which statement is correct?
- 15
A
CASEexpression has noELSEclause and noWHENcondition matches. What does it return? - 16
Which SQL function returns the current date (date only, no time) in standard SQL?
- 17
Why should you avoid
NOT IN (subquery)when the subquery might return NULL values? - 18
Which of the following correctly uses
EXISTSto find students enrolled in course 2?
Coding exercises
9 tasksList Students by City
EasyWrite a query that returns the name and city of every student, ordered alphabetically by city and then by name within each city.
SELECT -- your columns
FROM students
ORDER BY -- your sort columns;💡 Show hint
You can pass multiple columns to ORDER BY, separated by commas.
✅ Show solution
SELECT name, city
FROM students
ORDER BY city ASC, name ASC;Find Students with a Specific Pattern
EasyReturn the id and name of all students whose name contains the substring 'an' (case-insensitive match is not required — just use LIKE). Limit the results to the first 5 rows.
💡 Show hint
Use %an% as your LIKE pattern and add a LIMIT clause.
✅ Show solution
SELECT id, name
FROM students
WHERE name LIKE '%an%'
LIMIT 5;Count Students per City
EasyWrite a query that shows each city and the number of students in that city. Name the count column total. Only include cities that have more than 2 students.
SELECT city, COUNT(*) AS total
FROM students
GROUP BY city
-- filter groups here💡 Show hint
Use HAVING (not WHERE) to filter on the aggregate count.
✅ Show solution
SELECT city, COUNT(*) AS total
FROM students
GROUP BY city
HAVING COUNT(*) > 2;Enrolment Report with JOIN
MediumUsing the students and enrolments tables, write a query that returns each student's name and the course_id they are enrolled in. Include all students even if they have no enrolments (show NULL for course_id in that case). Order by student name.
💡 Show hint
Use a LEFT JOIN so that students without enrolments still appear.
✅ Show solution
SELECT s.name, e.course_id
FROM students s
LEFT JOIN enrolments e ON s.id = e.student_id
ORDER BY s.name;Update and Handle NULLs
MediumSome students have a NULL value in the level column. Write two separate statements:
1. An UPDATE that sets level = 1 for every student where level is NULL.
2. A SELECT that returns the name and level of all students where level is still NULL after the update (should return no rows, but the query must be valid).
💡 Show hint
Use IS NULL (not = NULL) to check for NULL values in a WHERE clause.
✅ Show solution
UPDATE students
SET level = 1
WHERE level IS NULL;
SELECT name, level
FROM students
WHERE level IS NULL;Subquery: Above-Average Level Students
HardWrite a single query (using a subquery) that returns the name and level of every student whose level is strictly above the average level across all students. Order results by level descending.
💡 Show hint
Compute AVG(level) in a subquery inside your WHERE clause: WHERE level > (SELECT AVG(level) FROM students).
✅ Show solution
SELECT name, level
FROM students
WHERE level > (
SELECT AVG(level)
FROM students
)
ORDER BY level DESC;Rank Students by Score per Course
MediumUsing a window function, write a query that returns student_id, course_id, score, and a rank column that ranks students within each course from highest score (rank 1) to lowest. Use RANK() so that tied scores receive the same rank. Only include rows where score is not NULL.
SELECT
student_id,
course_id,
score,
RANK() OVER (
-- partition and order here
) AS rank
FROM enrolments
WHERE score IS NOT NULL;💡 Show hint
Use PARTITION BY course_id ORDER BY score DESC inside the OVER clause.
✅ Show solution
SELECT
student_id,
course_id,
score,
RANK() OVER (
PARTITION BY course_id
ORDER BY score DESC
) AS rank
FROM enrolments
WHERE score IS NOT NULL;CTE: Courses with Below-Average Enrolments
MediumWrite a query using a CTE named course_counts that counts the number of enrolments per course. Then in the main query, return the course_id and enrolment_count for courses that have fewer enrolments than the average enrolment count across all courses.
💡 Show hint
Compute COUNT(*) per course_id in the CTE, then compare against AVG(enrolment_count) in a subquery or second CTE.
✅ Show solution
WITH course_counts AS (
SELECT course_id, COUNT(*) AS enrolment_count
FROM enrolments
GROUP BY course_id
)
SELECT course_id, enrolment_count
FROM course_counts
WHERE enrolment_count < (
SELECT AVG(enrolment_count) FROM course_counts
)
ORDER BY enrolment_count ASC;CASE: Grade Band Report
HardWrite a query that returns student_id, course_id, score, and a grade column computed with a CASE expression: scores 90 and above are 'A', 75-89 are 'B', 60-74 are 'C', any other non-NULL score is 'F', and NULL scores should show 'Not graded'. Order by student_id, then course_id.
SELECT
student_id,
course_id,
score,
CASE
-- your conditions here
END AS grade
FROM enrolments
ORDER BY student_id, course_id;💡 Show hint
Put the most specific conditions (>= 90) first. Use WHEN score IS NOT NULL THEN 'F' before the ELSE to catch remaining non-NULL scores.
✅ Show solution
SELECT
student_id,
course_id,
score,
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 75 THEN 'B'
WHEN score >= 60 THEN 'C'
WHEN score IS NOT NULL THEN 'F'
ELSE 'Not graded'
END AS grade
FROM enrolments
ORDER BY student_id, course_id;