CPCODELAB
Practice

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
Score: 0 / 18
0/18 answered
  1. 1

    What does SELECT DISTINCT city FROM students; return?

  2. 2

    Which clause filters rows after aggregation?

  3. 3

    Given students(id, name, level, city), what does this return? SELECT COUNT(*) FROM students WHERE city = 'London';

  4. 4

    Which SQL clause limits the number of rows returned?

  5. 5

    What does an INNER JOIN between students and enrolments on student_id return?

  6. 6

    What does LEFT JOIN guarantee that INNER JOIN does not?

  7. 7

    Which WHERE condition finds students whose name starts with the letter M?

  8. 8

    What does SELECT AVG(level) FROM students; return when the level column has some NULL values?

  9. 9

    Which statement correctly inserts a new row?

  10. 10

    What is the purpose of a primary key constraint?

  11. 11

    Which query returns students not enrolled in any course (assuming enrolments.student_id references students.id)?

  12. 12

    What does adding an index on students(city) primarily improve?

  13. 13

    What does ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY score DESC) do?

  14. 14

    In a CTE (WITH clause), which statement is correct?

  15. 15

    A CASE expression has no ELSE clause and no WHEN condition matches. What does it return?

  16. 16

    Which SQL function returns the current date (date only, no time) in standard SQL?

  17. 17

    Why should you avoid NOT IN (subquery) when the subquery might return NULL values?

  18. 18

    Which of the following correctly uses EXISTS to find students enrolled in course 2?

🛠️

Coding exercises

9 tasks
1

List Students by City

Easy

Write a query that returns the name and city of every student, ordered alphabetically by city and then by name within each city.

Starter
sql
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
sql
SELECT name, city
FROM students
ORDER BY city ASC, name ASC;
2

Find Students with a Specific Pattern

Easy

Return 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
sql
SELECT id, name
FROM students
WHERE name LIKE '%an%'
LIMIT 5;
3

Count Students per City

Easy

Write 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.

Starter
sql
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
sql
SELECT city, COUNT(*) AS total
FROM students
GROUP BY city
HAVING COUNT(*) > 2;
4

Enrolment Report with JOIN

Medium

Using 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
sql
SELECT s.name, e.course_id
FROM students s
LEFT JOIN enrolments e ON s.id = e.student_id
ORDER BY s.name;
5

Update and Handle NULLs

Medium

Some 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
sql
UPDATE students
SET level = 1
WHERE level IS NULL;

SELECT name, level
FROM students
WHERE level IS NULL;
6

Subquery: Above-Average Level Students

Hard

Write 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
sql
SELECT name, level
FROM students
WHERE level > (
  SELECT AVG(level)
  FROM students
)
ORDER BY level DESC;
7

Rank Students by Score per Course

Medium

Using 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.

Starter
sql
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
sql
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;
8

CTE: Courses with Below-Average Enrolments

Medium

Write 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
sql
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;
9

CASE: Grade Band Report

Hard

Write 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.

Starter
sql
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
sql
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;