All GCSE 5 40 min read 10 question quiz 14 exam marks

Databases and SQL

Tables, records, fields and keys, then SQL that actually runs: SELECT, WHERE, LIKE, ORDER BY, INSERT, UPDATE and DELETE, and how a Python program talks to a database without being hacked.

Tables, records, fields and keys

A database is an organised collection of data. At GCSE it is made of tables, and a table is a grid where:

  • each column is a field, a single kind of fact, with a data type
  • each row is a record, all the facts about one thing
  • one field is chosen as the primary key, whose value is unique to each record
StudentIDNameYearMark
1Asha1056
2Ben1171
3Chloe1038

StudentID is the primary key. There may be two students called Asha, and two with a mark of 56, but never two with the same StudentID, so a key finds exactly one record.

Why a key and not a name

Anything that could be repeated cannot identify a record. Names, dates of birth and marks all repeat. That is why every table is given a number or a code that is unique by design.

A real database in Python

Python has SQLite built in. ":memory:" makes a database that exists only while the program runs, which is ideal for learning. Everything in this topic can be run with the button.

Python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE Student (StudentID INTEGER PRIMARY KEY, Name TEXT, Year INTEGER, Mark INTEGER)")
db.executemany("INSERT INTO Student VALUES (?, ?, ?, ?)", [
    (1, "Asha", 10, 56),
    (2, "Ben", 11, 71),
    (3, "Chloe", 10, 38),
    (4, "Dev", 11, 90),
    (5, "Esme", 10, 64),
    (6, "Alan", 11, 45),
])

for record in db.execute("SELECT * FROM Student"):
    print(record)

Each record comes back as a tuple, in the order of the fields. The data types SQLite uses are INTEGER for whole numbers, REAL for decimals and TEXT for strings.

Searching with SELECT

The exam asks you to search a table with SELECT. The three parts, always in this order:

ClauseWhat it saysExample
SELECTWhich fields to showSELECT Name, Mark
FROMWhich tableFROM Student
WHEREWhich records to includeWHERE Mark >= 60

SELECT * means every field. A query with no WHERE returns every record.

Reading a SELECT queryHow one SELECT query works on the Student table of six students. FROM Student starts with the whole table. WHERE Year equals 11 AND Mark is at least 60 keeps only the rows for Ben, who is in Year 11 and scored 71, and Dev, who is in Year 11 and scored 90. Alan is in Year 11 but scored 45, and Esme scored 64 but is in Year 10, so neither is kept. SELECT Name, Mark then shows only those two columns, giving the result Ben 71 and Dev 90.SELECT Name, Mark FROM Student WHERE Year = 11 AND Mark >= 60StudentIDNameYearMark1Asha10562Ben11713Chloe10384Dev11905Esme10646Alan1145StudentresultNameMarkFROM: look in this tableFROM: look in this tableWHERE: keep only these rowsFROM: look in this tableWHERE: keep only these rowsSELECT: show only these columnsBen71Dev90
Read a query in the order the computer does: FROM, then WHERE, then SELECT.

This program sets the same table up and runs five queries, printing each query and its result. Run it and match each result to the table.

Python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE Student (StudentID INTEGER PRIMARY KEY, Name TEXT, Year INTEGER, Mark INTEGER)")
db.executemany("INSERT INTO Student VALUES (?, ?, ?, ?)", [
    (1, "Asha", 10, 56), (2, "Ben", 11, 71), (3, "Chloe", 10, 38),
    (4, "Dev", 11, 90), (5, "Esme", 10, 64), (6, "Alan", 11, 45),
])

def run(sql):
    print(sql)
    for record in db.execute(sql):
        print("    ", record)

run("SELECT Name, Mark FROM Student WHERE Mark >= 60")
run("SELECT * FROM Student WHERE Year = 10 AND Mark < 50")
run("SELECT Name FROM Student WHERE Year = 11 OR Mark > 60")
run("SELECT Name FROM Student WHERE Name LIKE 'A%'")
run("SELECT Name, Mark FROM Student ORDER BY Mark DESC")

The operators in a WHERE

NeedWriteNotes
Compare= <> < <= > >=One equals sign, not two. Not equal is <>
BothANDYear = 10 AND Mark < 50
EitherORYear = 11 OR Mark > 60
PatternLIKE% matches any run of characters
OrderORDER BYASC is the default, DESC is highest first
Text goes in quotes, numbers do not

WHERE Name = 'Asha' but WHERE Year = 10. Wrong the other way round and the query either fails or silently finds nothing.

LIKE 'A%' means "starts with A". '%a' ends with a, and '%an%' contains an anywhere. That is the whole of pattern matching at GCSE.

An SQL query labelled clause by clause, and what each clause does to the tableAn SQL query is read clause by clause. SELECT name, mark chooses which columns. FROM results says which table. WHERE mark greater than 60 chooses which rows. ORDER BY mark DESC puts them in order, highest first. Applied to a table of five pupils, id, name and mark, WHERE drops Ben on 55 and Eli on 48 and keeps three rows. SELECT keeps only the name and mark columns. ORDER BY puts the result in order: Chloe 91, Aisha 72 and Dev 64.query.sqlSELECT name, markFROM resultsWHERE mark > 60ORDER BY mark DESC;which columnswhich tablewhich rowsin what order1The table: results2idnamemark1Aisha722Ben553Chloe914Dev645Eli48WHERE mark > 60 keeps 3 rows and drops 2The result3namemarkChloe91Aisha72Dev64only the two chosen columns,highest mark first
  1. Each clause has a job: SELECT picks columns, FROM names the table, WHERE picks rows, ORDER BY sorts.
  2. WHERE keeps the rows that meet the condition and drops the rest.
  3. The result has only the selected columns, in the order asked for.
Read a query in the order FROM, WHERE, SELECT, ORDER BY: pick the table, filter the rows, choose the columns, then sort.

Changing data: INSERT, UPDATE and DELETE

Three statements change the table. All three come up in the exam.

OCR reference language
INSERT INTO Student (StudentID, Name, Year, Mark) VALUES (7, 'Femi', 10, 58)
UPDATE Student SET Mark = Mark + 5 WHERE Year = 10
DELETE FROM Student WHERE Mark < 45
Forget the WHERE and it applies to every record

UPDATE Student SET Mark = 0 sets every mark in the whole table to zero, and DELETE FROM Student empties it. There is no undo. Always write the WHERE first, then the rest.

The program below counts the records each statement touches, which is a good habit before trusting an UPDATE or DELETE.

Python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE Student (StudentID INTEGER PRIMARY KEY, Name TEXT, Year INTEGER, Mark INTEGER)")
db.executemany("INSERT INTO Student VALUES (?, ?, ?, ?)", [
    (1, "Asha", 10, 56), (2, "Ben", 11, 71), (3, "Chloe", 10, 38),
    (4, "Dev", 11, 90), (5, "Esme", 10, 64), (6, "Alan", 11, 45),
])

db.execute("INSERT INTO Student (StudentID, Name, Year, Mark) VALUES (7, 'Femi', 10, 58)")

changed = db.execute("UPDATE Student SET Mark = Mark + 5 WHERE Year = 10").rowcount
print(changed, "records updated")

removed = db.execute("DELETE FROM Student WHERE Mark < 45").rowcount
print(removed, "record deleted")

for record in db.execute("SELECT Name, Mark FROM Student WHERE Year = 10"):
    print(record)

Year 10 had four students once Femi joined, so four records were updated. Chloe's 38 became 43, which is under 45, so she was then deleted.

Two tables and a foreign key

A school has far more than students: subjects, results, teachers. Putting every fact in one table repeats the same details over and over. A relational database splits the facts across several tables and links them with a foreign key, a field in one table that holds the primary key of another.

Python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE Student (StudentID INTEGER PRIMARY KEY, Name TEXT)")
db.execute('''CREATE TABLE Result (
    ResultID INTEGER PRIMARY KEY,
    StudentID INTEGER,
    Subject TEXT,
    Mark INTEGER,
    FOREIGN KEY (StudentID) REFERENCES Student(StudentID))''')
db.executemany("INSERT INTO Student VALUES (?, ?)", [(1, "Asha"), (2, "Ben")])
db.executemany("INSERT INTO Result VALUES (?, ?, ?, ?)", [
    (1, 1, "Computing", 56), (2, 1, "Maths", 71),
    (3, 2, "Computing", 64), (4, 2, "Maths", 48),
])

sql = '''SELECT Student.Name, Result.Subject, Result.Mark
         FROM Student JOIN Result ON Student.StudentID = Result.StudentID
         WHERE Result.Mark >= 60'''
for record in db.execute(sql):
    print(record)

StudentID in Result is the foreign key. Asha's name is stored once, however many results she has. A JOIN joins the tables back together for a query. Joins go beyond what the GCSE examines, but the key idea, that a foreign key links two tables, does not.

SQL from a program, safely

Real programs build queries out of what the user typed. That is dangerous if done carelessly. Suppose a program looks a student up by name, and somebody types this as the name:

x' OR '1'='1

Joined into the query as text, the query becomes SELECT * FROM Student WHERE Name = 'x' OR '1'='1', and '1'='1' is always true, so every record is returned. This is SQL injection.

Python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE Student (StudentID INTEGER PRIMARY KEY, Name TEXT, Mark INTEGER)")
db.executemany("INSERT INTO Student VALUES (?, ?, ?)",
               [(1, "Asha", 56), (2, "Ben", 71), (3, "Chloe", 38)])

name = "x' OR '1'='1"

unsafe = "SELECT * FROM Student WHERE Name = '" + name + "'"
print(len(db.execute(unsafe).fetchall()), "records returned by the unsafe query")

safe = db.execute("SELECT * FROM Student WHERE Name = ?", (name,))
print(len(safe.fetchall()), "records returned by the safe query")

The ? is a placeholder. The database is given the query and the value separately, and treats the value only ever as data, never as part of the instruction. Whatever the user types, it cannot change what the query does.

Sanitise by never mixing

At GCSE you may be asked to describe sanitising input. In a database program the best sanitising is not to clean the text but to keep it out of the instruction altogether.

Key terms you must be able to define

Database
An organised collection of data that can be searched, sorted and updated.
Table
A grid in a database, with a column for each field and a row for each record.
Record
One row of a table, holding all the facts about one thing.
Field
One column of a table, holding one kind of fact.
Primary key
A field whose value is unique for every record, so it identifies exactly one.
Foreign key
A field in one table that holds the primary key of a record in another, linking the two.
SQL
Structured Query Language, used to search and change data in a relational database.
Wildcard
A symbol that stands for any characters in a pattern. In SQL LIKE, the % sign.
SQL injection
An attack in which text typed by a user is run as part of a database query.

Flashcards

Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.

DefineDatabase
AnswerAn organised collection of data that can be searched, sorted and updated.
DefineTable
AnswerA grid in a database, with a column for each field and a row for each record.
DefineRecord
AnswerOne row of a table, holding all the facts about one thing.
DefineField
AnswerOne column of a table, holding one kind of fact.
DefinePrimary key
AnswerA field whose value is unique for every record, so it identifies exactly one.
DefineForeign key
AnswerA field in one table that holds the primary key of a record in another, linking the two.
DefineSQL
AnswerStructured Query Language, used to search and change data in a relational database.
DefineWildcard
AnswerA symbol that stands for any characters in a pattern. In SQL LIKE, the % sign.

Reach the top grade on this topic

SQL questions are about precision: one wrong keyword costs the mark, and every clause is worth one.

  • Write SELECT, FROM and WHERE in the right order, and a field name exactly as the table gives it
  • Put quotation marks around text values and none around numbers
  • Use AND and OR to build conditions, and LIKE with the percent sign for a pattern
  • Never write UPDATE or DELETE without a WHERE unless every record is meant
  • Explain a primary key as unique, and a foreign key as the link to another table

Mistakes that cost marks here

Avoid these
  • Writing == in a WHERE clause. SQL uses a single = to compare.
  • Leaving out the quotation marks around a text value, or adding them round a number.
  • Writing UPDATE or DELETE with no WHERE, which changes every record.
  • Describing a primary key as the first field, when it is the field that is unique.
  • Joining user input into the query text instead of using placeholders.
Fun fact

Python ships with a complete SQL database engine, SQLite, built in. It is probably the most widely deployed database in the world, and one sits inside every Android phone, every iPhone and every copy of Chrome and Firefox.

Knowledge check

0 of 10 answered
1What is a primary key?
2What is one row of a table called?
3What does SELECT Name FROM Student WHERE Mark > 50 return?
4Which wildcard in a LIKE pattern matches any run of characters?
5Which clause shows the highest marks first?
6What does UPDATE Student SET Mark = 0 do when it has no WHERE clause?
7Which statement removes records from a table?
8What is a foreign key?
9Why should a Python program use ? placeholders in an SQL query?
10What does the * in SELECT * FROM Student mean?
Score 0 / 10

Exam-style questions

Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.

14 marks
1State what is meant by a primary key, and why every table needs one.2 marks
State

Mark scheme0 / 2
  • LA field that holds a value that is unique to each record
  • LSo that each record can be identified or found without confusion
Model answer

A primary key is a field whose value is unique to each record in the table. Every table needs one so that each record can be identified exactly, because other fields such as a name could be repeated.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2A table called Book has the fields BookID, Title, Author and Pages. Write an SQL query that displays the title of every book that has more than 300 pages, with the book with the most pages first.3 marks
Write

Mark scheme0 / 3
  • LSELECT Title FROM Book
  • LWHERE Pages > 300
  • LORDER BY Pages DESC
Model answer
SELECT Title
FROM Book
WHERE Pages > 300
ORDER BY Pages DESC

SELECT names the field to display and FROM the table. WHERE restricts the records to those with more than 300 pages, and ORDER BY with DESC puts the longest book first.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3Write an SQL statement that changes the author of the book with BookID 7 to the text A. Khan.3 marks
Write

Mark scheme0 / 3
  • LUPDATE Book
  • LSET Author = 'A. Khan'
  • LWHERE BookID = 7
Model answer
UPDATE Book
SET Author = 'A. Khan'
WHERE BookID = 7

The WHERE clause is essential. Without it every book in the table would be given A. Khan as its author.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
4The records in the Student table are shown. State the result of the query SELECT Name FROM Student WHERE Year = 11 AND Mark >= 60.2 marks
State
StudentIDNameYearMark
1Asha1056
2Ben1171
3Chloe1038
4Dev1190
5Esme1064
6Alan1145

Mark scheme0 / 2
  • LBen is included
  • LDev is included, and no other names
Model answer

Only Ben and Dev are in Year 11 with a mark of 60 or more. Alan is in Year 11 but scored 45, and Esme scored 64 but is in Year 10.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5A program asks the user to type a name and joins it into an SQL query using string concatenation. Explain the security risk, and describe how it should be done instead.4 marks
Explain

Mark scheme0 / 4
  • LThe user could type SQL code instead of a name
  • LThis would be run as part of the query, so data could be read or changed that should not be
  • LUse placeholders or a parameterised query
  • LThe value is passed separately so it is treated only as data
Model answer

A user could type SQL code into the name box instead of a name. Because the text is joined into the query, it is run as part of the instruction, which is called SQL injection, and it could return every record or change or delete data. The query should instead use a placeholder, or parameterised query, where the value is passed to the database separately, so whatever is typed is treated only as data and never as part of the query.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
Machine mark 0 / 14