- Understand what SQL is and what the SELECT statement does
- Write SELECT queries using FROM and WHERE clauses
- Use comparison operators (
=,<,>) in WHERE conditions - Combine conditions using AND and OR operators
- Sort results using ORDER BY with up to two fields
- Write equi-joins to retrieve data from two linked tables
- I can write a SELECT query to retrieve specific fields from one table
- I can add a WHERE clause to filter results based on conditions
- I can combine multiple conditions using AND and OR
- I can use comparison operators to find records matching a range (e.g. goals > 20)
- I can sort results using ORDER BY in ascending or descending order
- I can write an equi-join between two tables using primary and foreign keys
Open the Handball database:
- Open DB Browser for SQLite
- Click File → Open Database
- Navigate to the databases folder and select handball.db (relative path:
../databases/handball.db) - Click the Execute SQL tab at the top
- You're ready to start the practical tasks!
💡 Keep DB Browser open throughout the lesson — you'll be writing and running SQL queries in every practical task.
Download handball.sql and import it into DB Browser for SQLite before the warm-up. You'll need this database open for all the practical tasks. If you haven't set up DB Browser yet, follow the setup guide first.
Answer before the lesson begins. These check your knowledge of database structure.
1. What does SQL stand for?
2. Which SQL keyword do you use to choose which fields to display in the results?
Key vocabulary
goalsScored > 20).WHERE Team.teamName = Player.teamName).The Handball Database
This lesson uses the Scottish Handball League database with two linked tables. You will refer back to this throughout.
| Player | ||||||
|---|---|---|---|---|---|---|
| playerID | firstname | surname | dateOfBirth | position | goalsScored | teamName FK |
| 810JE | Jack | Edwards | 03/05/1994 | Left back | 37 | Clyde Flyers |
| Team | ||||||
|---|---|---|---|---|---|---|
| teamName PK | town | leagueDivision | district | trainingVenue | contactPerson | emailAddress |
| Clyde Flyers | Greenock | 1 | West | Burnside Sports Centre | Chris Black | clydeflyers@handball.mail.uk |
SQL SELECT — Querying a Database
What is SQL?
SQL (Structured Query Language) is a standard language for retrieving and managing data in relational databases. When you run a query in DB Browser for SQLite, you are writing SQL. Every time a web app searches for your name, every time a bank finds your account, every time a shop searches its inventory — they are all using SQL queries to get data from databases. In this course we focus on the SELECT statement, which is the most common SQL command.
The basic SELECT structure
Every SELECT query follows the same pattern:
FROM tableName
WHERE fieldName = value;
Line 1 (SELECT): Tells the database which fields (columns) to show. You can list specific fields separated by commas, or use * to select all fields.
Line 2 (FROM): Tells the database which table to search in.
Line 3 (WHERE): Sets conditions to filter the results. Only records where the condition is true are returned. (The WHERE clause is optional — if you omit it, all records are returned.)
Text values vs numbers in WHERE
This distinction is crucial: when you search for text (like a player's position), the value must be in double quotes. When you search for numbers, no quotes are used.
FROM Player
WHERE position = "Left back";
FROM Player
WHERE goalsScored > 20;
Comparison operators in WHERE
The WHERE clause uses comparison operators to match records. Here are the main ones:
| Operator | Meaning | Example |
|---|---|---|
= | Equal to | position = "Left back" |
< | Less than | goalsScored < 10 |
> | Greater than | goalsScored > 20 |
<= | Less than or equal to | goalsScored <= 25 |
>= | Greater than or equal to | goalsScored >= 30 |
<> | Not equal to | position <> "Goalkeeper" |
Practical Task 1 of 5 — Running your first SELECT query
What to do: Write and run SELECT queries to retrieve player data, starting simple then adding a WHERE clause.
FROM Player;
FROM Player
WHERE goalsScored > 20;
📝 Capture your evidence here
- Your SQL queries pasted above
- Screenshots of both result sets captured above
- Ready to export to PDF for your evidence portfolio
AND and OR — combining conditions
Often you need to filter by more than one condition. Use AND when both conditions must be true. Use OR when either condition can be true.
AND example: Find all players who play in the West district AND scored more than 10 goals.
FROM Player, Team
WHERE Team.district = "West"
AND Player.goalsScored > 10;
OR example: Find all players who play as a centre OR a left wing.
FROM Player
WHERE position = "Centre"
OR position = "Left wing";
Practical Task 2 of 5 — AND and OR conditions
What to do: Write queries using OR and AND operators to combine multiple conditions.
FROM Player
WHERE position = "Left back"
OR position = "right wing";
FROM Player
WHERE position = "Left back"
AND goalsScored > 15;
📝 Capture your evidence here
- Both SQL queries pasted above
- Screenshots of both result sets captured above
ORDER BY — sorting results
By default, results appear in the order stored in the database. To sort, use ORDER BY at the end of your query. You can sort by up to two fields at N5. ASC means ascending (A→Z, 0→9); DESC means descending (Z→A, 9→0). If you don't specify, ASC is the default.
FROM Player
ORDER BY goalsScored DESC, surname ASC;
This sorts players by goals scored (highest first). Where two players have the same number of goals, those records are sorted alphabetically by surname (A→Z).
Practical Task 3 of 5 — Sorting results with ORDER BY
What to do: Write a query that sorts player data by two fields.
FROM Player
ORDER BY goalsScored DESC, surname ASC;
Notice how the results are first sorted by highest goals scored. Where two players have the same score, those two are sorted alphabetically by surname.
📝 Capture your evidence here
- Your ORDER BY query pasted above
- Screenshot showing the two-field sort results
Equi-joins — combining data from two tables
This is the most difficult part of this lesson. When you need data from two linked tables, you must join them using the primary key = foreign key pattern. Without the join, you get incorrect results.
The join pattern: In your WHERE clause, match the primary key from one table to the foreign key from the other.
FROM Table1, Table2
WHERE Table1.primaryKey = Table2.foreignKey
AND otherCondition = value;
Example from the SQA specification: Find the firstname, surname, and town for all players in the West district.
FROM Team, Player
WHERE Team.teamName = Player.teamName
AND district = "West";
Why the join is needed: The town field exists in the Team table. The firstname and surname fields exist in the Player table. To get all three in one query, you list both tables in FROM and link them in WHERE using Team.teamName = Player.teamName. This tells the database to only return player records whose teamName matches a team record's teamName.
Practical Task 4 of 5 — Joining two tables (CRITICAL)
What to do: Master equi-joins by writing and running queries that combine data from Player and Team tables.
FROM Team, Player
WHERE Team.teamName = Player.teamName
AND town = "Greenock";
Notice the WHERE clause has two parts: the join (Team.teamName = Player.teamName) AND the filter (town = "Greenock").
FROM Player, Team
WHERE Player.teamName = Team.teamName
AND district = "West";
📝 Capture your evidence here
- Both equi-join queries pasted above
- Screenshots of both result sets captured above
- ⚠ CRITICAL: Both queries include the equi-join WHERE clause
Worked examples
Question: Display the firstname, surname, and position of all players.
FROM Player;
Question: Find the firstname, surname, and goalsScored for all players who scored more than 20 goals.
FROM Player
WHERE goalsScored > 20;
Question: Find players who play as a Left back AND scored more than 10 goals.
FROM Player
WHERE position = "Left back"
AND goalsScored > 10;
Question: List all players sorted by goalsScored (highest first), then by surname (A→Z).
FROM Player
ORDER BY goalsScored DESC, surname ASC;
Question: Find the firstname, surname, and town for all players in the West district.
FROM Team, Player
WHERE Team.teamName = Player.teamName
AND district = "West";
Write a SQL query to: Display firstname, surname, and goalsScored for all players who are NOT goalkeepers, sorted by goalsScored in descending order.
FROM Player
WHERE position <> "Goalkeeper"
ORDER BY goalsScored DESC;
Explanation: We use <> (not equal to) to exclude goalkeepers. The ORDER BY goalsScored DESC sorts from highest to lowest goals scored.
FROM Team, Player), your WHERE clause MUST include the join line (WHERE Team.teamName = Player.teamName) before any other conditions. Without it, the database returns nonsense — every combination of every player with every team.
position = "Left back". Numbers never use quotes: goalsScored > 20. Getting this wrong usually causes a database error.
WHERE position = "Left wing" AND position = "Centre", you will get zero results (a player cannot be both positions). You need OR.
Team.teamName = Player.teamName, not just teamName = teamName. Otherwise the database does not know which table's teamName you mean.
The #1 reason candidates lose marks: forgetting the equi-join line. When the question asks for data from two tables, you MUST include the join in the WHERE clause. Mark schemes consistently award zero if this line is missing, even if the rest of the query is correct.
Check your WHERE clause: if it lists two tables in FROM, the first line of WHERE must be the equi-join. Only then should you add filtering conditions with AND.
Source: SQA Course Reports 2023, 2024, 2025 — consistently identify missing equi-joins as a major cause of lost marks in database questions.
Practical Task 5 of 5 — Consolidation Challenge
What to do: Write a single query from scratch (no hints) that combines everything you've learned today.
Think about what you need:
- Which fields do you need? (firstname, surname, goalsScored, town)
- Which tables? (Player has firstname/surname/goalsScored; Team has town)
- Do you need a join? (Yes — because town is in Team)
- What's the filter? (goalsScored > 10)
- What's the sort? (goalsScored DESC)
Expected solution structure:
SELECT firstname, surname, goalsScored, town
FROM ... , ...
WHERE ... = ...
AND goalsScored > 10
ORDER BY ...
📝 Capture your evidence here
- Your completed query pasted above
- Screenshot of results (players with 10+ goals, sorted by goals DESC)
- ✨ This is your evidence for today's SQL lesson!
Questions 1–4 are auto-checked. Questions 5–10 are self-marked — write your answer, then reveal the model answer to check your work.
1. Which SQL keyword specifies which fields to display in the query results? TYPE 1
2. What does ORDER BY goalsScored DESC do? TYPE 1
3. In the WHERE clause, how do you write a condition to find players with a position of "Left back"? TYPE 1
4. What is the primary purpose of an equi-join in a SQL query? TYPE 1
5. Write a SQL query to display firstname, surname, and position for all players who scored more than 15 goals. TYPE 2
SELECT firstname, surname, position
FROM Player
WHERE goalsScored > 15;
6. Write a SQL query to find all players who play in position "Centre" OR "Left wing", displaying their surname and position, sorted by surname A→Z. TYPE 2
SELECT surname, position
FROM Player
WHERE position = "Centre"
OR position = "Left wing"
ORDER BY surname ASC;
Note: We use OR because a player can be in ONE position or the other (not both). If we used AND, we would get zero results.
7. Write a SQL query to find the firstname, surname, and goalsScored for all players in league division 1, sorted by goalsScored (highest first). This requires joining the Player and Team tables. TYPE 2
SELECT firstname, surname, goalsScored
FROM Player, Team
WHERE Player.teamName = Team.teamName
AND leagueDivision = 1
ORDER BY goalsScored DESC;
Key point: The WHERE clause has TWO parts. First, the equi-join (Player.teamName = Team.teamName). Second, the filter condition (leagueDivision = 1), joined with AND.
8. Identify the error in this query. Explain what is wrong and how to fix it.SELECT firstname, surname FROM Team, Player WHERE district = "West"; TYPE 2
WHERE Team.teamName = Player.teamName, the database returns every combination of every player with every team — nonsense results.Fix: Add the equi-join as the first condition in WHERE:
SELECT firstname, surname
FROM Team, Player
WHERE Team.teamName = Player.teamName
AND district = "West";
9. Write a SQL query to display the firstname, surname, position, and town for all players who scored more than 25 goals AND play in the West district. TYPE 3
SELECT firstname, surname, position, town
FROM Player, Team
WHERE Player.teamName = Team.teamName
AND goalsScored > 25
AND district = "West";
10. Using the Handball database, write a query to find all teams (displaying teamName and town) in league division 2 that are in the North district. Write the SQL query and describe what data it will retrieve. TYPE 3
SELECT teamName, town
FROM Team
WHERE leagueDivision = 2
AND district = "North";
Description: This query retrieves the names and towns of all teams that compete in league division 2 AND are located in the North district. No equi-join is needed because we are only selecting from one table (Team). The query returns only records where BOTH conditions are true.
Suggested timing: 75 minutes total. Warm up + vocabulary 10 min; notes with live query demos 25 min; worked examples 15 min; now you try 5 min; task set 20 min.
Prerequisite: Pupils should have DDD1 and DDD2 mastered (understand tables, records, fields, primary keys, foreign keys, and ERDs).
Key misconception to address: The equi-join. This is consistently the #1 error in exam papers. Many candidates omit the join line entirely when querying two tables, resulting in zero marks. Use live demos in DB Browser to show the difference: run the query without the join (catastrophic results) versus with it (correct results). Make this visually dramatic.
Live demo checklist: (1) Open the Handball database in DB Browser. (2) Run a simple SELECT with WHERE to show filtering. (3) Show ORDER BY with one field, then two fields. (4) Most importantly: demonstrate a two-table query WITHOUT the join (show nonsense output), then add the join line and re-run (show correct output). This visual contrast is powerful.
Common error patterns to watch for: (1) Forgetting quotes around text values. (2) Using AND instead of OR. (3) Omitting the equi-join. (4) Forgetting table names in the join (writing teamName = teamName instead of Team.teamName = Player.teamName).
Extension questions: (1) Can you write a three-field ORDER BY? (Not at N5, but good to explain the limitation.) (2) What happens if you use SELECT * in a join? (You get all fields from both tables.) (3) Why can't you use WHERE teamName = Player.teamName instead of full names? (Ambiguity — the database doesn't know which table's teamName you mean.)
SQA commands covered: "Write a query...", "Identify the error...", "Explain...". Remind pupils that "explain" needs a reason, not just the code.