SQL in a Unit 4 context
SQL in a Unit 4 context
Learning intention
WALT
We are learning to use SQL to store, retrieve and transform data exchanged by a digital solution.
WILF
- Read a relational schema and trace primary and foreign keys.
- Construct SELECT queries with filtering, grouping and ordering.
- Use inner joins and subqueries for related data.
- Modify structures and records safely and explain their application context.
Cognitive verbs
- Recognise: identify facts or characteristics from what you know.
- Analyse: break down information to reveal relationships.
- Determine: use evidence or reasoning to reach a conclusion.
- Develop: create a complete result by combining relevant ideas and processes.
- Explain: make an idea or relationship clear by showing how or why.
- Evaluate: make a judgment about quality using evidence.
SQL behind a data exchange
A client might send JSON to an API, but the back end may validate the request and use SQL to read or update a relational database.
mobile app → HTTPS/JSON → API validation → parameterised SQL → database
mobile app ← JSON response ← result transformation ← SQL result
Separation of concerns: SQL manages relational data. The API controls authentication, authorisation, validation, error handling and the representation returned to the client.
Use a small relational schema
VENUE(
venue_id PRIMARY KEY,
venue_name,
suburb,
accessible
)
EVENT( event_id PRIMARY KEY, venue_id FOREIGN KEY REFERENCES VENUE(venue_id), title, event_date, capacity, status )
BOOKING( booking_id PRIMARY KEY, event_id FOREIGN KEY REFERENCES EVENT(event_id), attendee_email, places, booking_status )
Keys connect records without repeating all venue and event data in every booking.
Trace a relationship
Explain how a BOOKING record is connected to a VENUE.
Check your answer
BOOKING.event_id links to EVENT.event_id. That EVENT record contains venue_id, which links to VENUE.venue_id. The relationship therefore passes through EVENT.
SELECT, WHERE and ORDER BY
SELECT event_id, title, event_date
FROM EVENT
WHERE status = 'open'
AND event_date >= '2026-08-05'
ORDER BY event_date ASC, title ASC;
SELECTchooses columns.FROMidentifies the table.WHEREfilters rows before grouping.ORDER BYsorts the result;ASCis ascending andDESCdescending.
Text and date literals use quotes. Numeric and Boolean syntax depends on the database system and schema.
Write a query
Develop a query that returns the title and capacity of cancelled events, largest capacity first.
Check your answer
SELECT title, capacity
FROM EVENT
WHERE status = 'cancelled'
ORDER BY capacity DESC;Conditions and IN
SELECT event_id, title
FROM EVENT
WHERE status IN ('open', 'full')
AND capacity >= 20;
IN tests membership of a set. Combine conditions with AND, OR and parentheses so the intended logic is unambiguous.
NULL needs special handling: use IS NULL or IS NOT NULL, not = NULL.
Aggregate functions and GROUP BY
Aggregate functions summarise rows: COUNT, MIN, MAX, AVG and SUM.
SELECT event_id, SUM(places) AS booked_places
FROM BOOKING
WHERE booking_status = 'confirmed'
GROUP BY event_id;
Each output row represents one event group. Non-aggregated selected columns normally need to appear in GROUP BY.
Count by status
Develop a query that counts bookings for each booking_status.
Check your answer
SELECT booking_status, COUNT(*) AS booking_count
FROM BOOKING
GROUP BY booking_status
ORDER BY booking_status;WHERE and HAVING do different jobs
WHEREfilters individual rows before groups are formed.HAVINGfilters groups after aggregation.
SELECT event_id, SUM(places) AS booked_places
FROM BOOKING
WHERE booking_status = 'confirmed'
GROUP BY event_id
HAVING SUM(places) >= 20;
The WHERE clause excludes non-confirmed rows. HAVING keeps only event groups whose confirmed places total at least 20.
INNER JOIN related tables
SELECT EVENT.title, VENUE.venue_name, VENUE.suburb
FROM EVENT
INNER JOIN VENUE
ON EVENT.venue_id = VENUE.venue_id
WHERE EVENT.status = 'open'
ORDER BY EVENT.title;
An inner join returns only rows with a matching relationship. Qualify column names with table names when ambiguity is possible.
Join three tables
Develop a query returning booking ID, event title and venue name for confirmed bookings.
View a sample solution
SELECT BOOKING.booking_id, EVENT.title, VENUE.venue_name
FROM BOOKING
INNER JOIN EVENT
ON BOOKING.event_id = EVENT.event_id
INNER JOIN VENUE
ON EVENT.venue_id = VENUE.venue_id
WHERE BOOKING.booking_status = 'confirmed';Subqueries
A subquery supplies a result to another query.
SELECT title, capacity
FROM EVENT
WHERE capacity > (
SELECT AVG(capacity)
FROM EVENT
);
The inner query calculates the average capacity. The outer query returns events above it.
Use a join when combining related columns and a subquery when one result naturally supplies a condition. Either may sometimes express the same goal; clarity and database behaviour matter.
Filter by a related set
Develop a subquery that returns event titles at accessible venues.
Check your answer
SELECT title
FROM EVENT
WHERE venue_id IN (
SELECT venue_id
FROM VENUE
WHERE accessible = TRUE
);The Boolean literal may differ across database systems.
CREATE and constraints
CREATE TABLE BOOKING (
booking_id INTEGER PRIMARY KEY,
event_id INTEGER NOT NULL,
attendee_email VARCHAR(254) NOT NULL,
places INTEGER NOT NULL CHECK (places > 0),
booking_status VARCHAR(20) NOT NULL,
FOREIGN KEY (event_id) REFERENCES EVENT(event_id)
);
Types and supported constraint syntax vary by database system. Constraints provide a final integrity layer but do not replace clear UI validation and server-side checks.
INSERT and UPDATE
INSERT INTO BOOKING
(booking_id, event_id, attendee_email, places, booking_status)
VALUES
(501, 42, '[email protected]', 2, 'confirmed');
UPDATE BOOKING
SET booking_status = 'cancelled'
WHERE booking_id = 501;
Check UPDATE scope: omitting or miswriting WHERE may change every row. Preview the target with an equivalent SELECT and use transactions or backups appropriate to the system.
Update a specific event
Develop an UPDATE that marks event 42 as full.
Check your answer
UPDATE EVENT
SET status = 'full'
WHERE event_id = 42;ALTER and DROP
ALTER TABLE EVENT
ADD registration_url VARCHAR(500);
ALTER changes an existing structure. DROP removes a database object, such as a table, and can cause irreversible data loss.
Before structural change, consider existing data, defaults, nullability, dependent queries and exchange contracts. Adding a required field without a value for old records can break the system.
Secure application queries
Never build SQL by directly joining untrusted input into a query string.
# Unsafe idea
query = "SELECT * FROM BOOKING WHERE attendee_email = '" + input + "'"
Safe pattern
query = "SELECT booking_id, event_id, places FROM BOOKING WHERE attendee_email = ?" ExecuteWithParameters(query, [input])
- Use parameterised queries or prepared statements.
- Grant the application database account only needed permissions.
- Authorise the user before querying protected records.
- Select only fields the response needs.
- Avoid exposing database errors or schema details to clients.
Parameters protect values, not arbitrary table names or SQL keywords. Dynamic structure requires a controlled allow-list.
Independent practice: event availability
Using the VENUE, EVENT and BOOKING schema:
- Develop a query returning open event titles, venue names and dates in accessible venues, earliest first.
- Develop a query returning each event ID and confirmed booked-place total, but only totals of 10 or more.
- Develop a subquery returning events whose capacity is greater than the maximum capacity of events at venue 3.
- Explain why attendee email should not be returned by a public events endpoint.
View a sample solution
SELECT EVENT.title, VENUE.venue_name, EVENT.event_date FROM EVENT INNER JOIN VENUE ON EVENT.venue_id = VENUE.venue_id WHERE EVENT.status = 'open' AND VENUE.accessible = TRUE ORDER BY EVENT.event_date ASC;SELECT event_id, SUM(places) AS booked_places FROM BOOKING WHERE booking_status = 'confirmed' GROUP BY event_id HAVING SUM(places) >= 10;SELECT event_id, title, capacity FROM EVENT WHERE capacity > ( SELECT MAX(capacity) FROM EVENT WHERE venue_id = 3 );- The public endpoint needs event availability, not attendee personal information. Excluding the email minimises disclosure and enforces separation between public and authorised booking data.
Review and exit ticket
- How do WHERE and HAVING differ?
- What does an inner join exclude?
- Why preview an UPDATE with SELECT?
- How do parameterised queries improve security?
Check your answers
- WHERE filters rows before grouping; HAVING filters aggregate groups.
- Rows without a matching join condition.
- It reveals which rows the condition targets before modification.
- They keep untrusted values separate from SQL structure.
Exit ticket
Write a query that returns all open events with capacity at least 30, ordered by title.