SQL and Query Planning
How relational data is stored and queried with SQL, how a query is translated into table operations, and how the cheapest query plan is chosen (joins, indexes, join ordering), plus transactions.
Concepts#
- Relational Modeltables as multisets of rows, primary and foreign keys start here
-
SQL Query Structureclauses
SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMITand their evaluation order -
Cartesian Productall row combinations; semantics of
FROM, columns add up and rows multiply - Relational Algebraprojection, selection, product and their cost/size estimates; query as sequence/tree of operations
- Join $T_1 \bowtie T_2$equi-join as a product + selection computed without the product; outer and natural joins, hash join
- Merge Joinjoin of sorted join columns by the Zipper algorithm
-
group-by(T, cols, agg)group-by with aggregation functions and
HAVING -
Ordering
ORDER BY,LIMIT,OFFSET -
Subquerya
SELECTquery used in place of a table - Query Plana sequence/expression/tree of operations with its estimated total cost
- B+ Treeindex with lookup, sorted leaves; basis of index-based joins
- Query Optimizationjoin graph and by dynamic programming over subsets
- ACIDtransaction guarantees (mentioned only, not exam-relevant)
How it fits together#
From a query to a result. A query SELECT … FROM … WHERE … is defined by a chain of operations on tables: the FROM tables are combined by the Cartesian Product, the WHERE expression selects rows (), the SELECT list projects columns (). GROUP BY/HAVING, ORDER BY/LIMIT and subqueries extend this chain; a subquery works because every operation takes tables and returns a table (Relational Model > Closure). This definition says what is computed, not how: the RDBMS is free to compute any equivalent plan.
From definition to efficient plan.
| Step | Problem | Remedy | Concept |
|---|---|---|---|
| 1 | Product materialises values | push and below the product; combine small tables first | Relational Algebra |
| 2 | Product + equality selection is still too large | single-column equi-join, never materialising the product | Join |
| 3 | Join needs a fast way to find matches | hash join (hash map), or merge join on sorted columns | Join, Merge Join |
| 4 | Sorted columns / fast lookups are needed | index B+ tree: lookup , leaves in sorted order | B+ Tree |
| 5 | Many tables: which join order? | join graph, by dynamic programming | Query Optimization |
Cost models by lecture. All of them are sums over the operations of a Query Plan, with sizes propagated from the leaves:
| Lecture | Operation | cost | rows | cols |
|---|---|---|---|---|
| VL04 | ||||
| VL04 | ||||
| VL04 | ||||
| VL05 | hash/merge join | |||
| VL05 | group-by | |||
| VL06 | join (total cost, size only) | – |
Which one to use depends on the task: the exam gives the VL04 estimates (select/project/product) for plan comparison; the VL06 model with is for join ordering of many tables.
Joins and indexes.
| Hash join | Merge Join | Index lookup (SQLite3) | |
|---|---|---|---|
| Needs | nothing | both join columns sorted | B+ Tree on one join column (temporary one if none) |
| Time | lookups of | ||
| Note | simple default | faster (); fed by B+ tree leaves | an index on both columns does not help further |
When SQL needs more than one clause. “Per group, a value” → Grouping; “per group, the row with the extreme value” (argmax) cannot be done by GROUP BY alone and needs a Subquery joined back to the table. WHERE filters rows before grouping, HAVING filters groups after aggregation. NATURAL JOIN joins on all equally named columns, so tracing a natural join with a subquery means checking which names the two tables share.
Related. Basic SPARQL queries translate mechanically into the joins above (SPARQL and Knowledge Graphs).
Key Formulas#
- , , (Relational Algebra)
- , (Cartesian Product)
- (Query Plan)
- , (Query Optimization)
- B+ tree lookup: depth , time (B+ Tree)
Exercises & Exam Questions#
SuccessSolution Q1
(a)
SELECT title FROM movies
WHERE release_year > 1900 AND release_year <= 2000;
(b)
SELECT movies.title, SUM(streaming.views) AS total_views
FROM movies, streaming
WHERE movies.id = streaming.movie_id
AND streaming.platform IN ("Netflix", "Amazon Prime")
GROUP BY movies.id
ORDER BY total_views DESC
LIMIT 10;
(c)
SELECT movies.title, GROUP_CONCAT(persons.name, ', ')
FROM movies, actors, persons
WHERE movies.id = actors.movie_id
AND actors.person_id = persons.id
GROUP BY movies.id
HAVING COUNT(actors.person_id) >= 10;
SuccessSolution Q2
Plan 1:
- select(A): (5 rows out)
- select(B): (10 rows out)
- cross product: (50 rows out)
- project:
Plan 2:
- cross product: (200 rows out)
- select: (100 rows out)
- select: (50 rows out)
- project:
Plan 2 can be cheaper if one table is empty (0 rows): then cross product costs 0 in Plan 2, but Plan 1 still pays for the selection on the non-empty table.
SuccessSolution Q3
- Subquery computes max score per year: (2021, 8.3), (2022, 8.2), (2023, 8.4)
- NATURAL JOIN matches on both
yearANDscore– only movies whose score equals the max for their year survive - Result after ORDER BY score DESC:
- (2023, Oppenheimer, 8.4), (2021, Spider-Man, 8.3), (2022, Top Gun, 8.2)
Natural language: For each year, the movie with the highest score, ordered by score descending.
SuccessSolution Q4
Computes each employee’s name together with the name of their supervisor (self-join on supervisor = id); is .
Original: .
Better: project both copies before the product: , , , , . Cost: .
SuccessSolution Q5
SELECT person
FROM founded_by NATURAL JOIN based_in NATURAL JOIN born_in;
The first natural join is on company, giving (company, person, city); joining born_in is on person and city. Equivalent: explicit JOIN … ON f.company = b.company and JOIN born_in n ON f.person = n.person AND b.city = n.city, or three tables in FROM with the three equalities in WHERE.
SuccessSolution Q6
(2) A subquery with GROUP BY e_id, title HAVING COUNT(*) > 1 yields the qualifying pairs, joined with employees:
SELECT DISTINCT e.name
FROM employees AS e, (
SELECT e_id, title FROM positions
GROUP BY e_id, title HAVING COUNT(*) > 1
) AS p
WHERE e.id = p.e_id;
(3) A subquery computes MAX(start_date) per employee and is joined back on employee and date:
SELECT e.name, p.title
FROM employees AS e, positions AS p, (
SELECT e_id, MAX(start_date) AS latest_date
FROM positions GROUP BY e_id
) AS pmax
WHERE e.id = p.e_id AND p.e_id = pmax.e_id
AND p.start_date = pmax.latest_date;
Exam occurrences#
| Semester | Task | Points | Subtopics |
|---|---|---|---|
| DBIS Exam WS1314 (not published) | T6 | 20 | SPARQL-to-SQL translation, efficient query processing with indexes |
| DBIS Exam WS1516 (not published) | T6 | 20 | SQL for knowledge base queries, inverted index size formula |
| DBIS Exam WS1617 (not published) | T6 | 20 | SQL for knowledge base, maximum entropy proof |
| DBIS Exam WS2324 (not published) | T2 | 20 | SQL queries on movie schema, query plan cost comparison |
| DBIS Exam WS2324 (not published) | T3 | 20 | SQL/SPARQL translation, NATURAL JOIN step-by-step |
| DBIS Exam WS2324-2 (not published) | T2 | 20 | SQL queries with subqueries on employees schema, self-join query plan cost |
| DBIS Exam WS2324-2 (not published) | T3 | 20 | SPARQL-to-SQL, subquery with GROUP BY and AVG |
Lectures#
- DBIS_VL03 — relational model, SQLite3, database design with keys,
SELECT … FROM … WHERE …via the cross-product - DBIS_VL04 — projection, selection, Cartesian product, query planning and cost estimates
- DBIS_VL05 — join (hash, merge, outer, natural), group-by and
HAVING,ORDER BY,LIMIT/OFFSET, subqueries - DBIS_VL06 — index B+ tree, optimal query plan by dynamic programming, ACID
- DBIS_VL13 — exam example task on query plan costs
Related Topics#
- SPARQL and Knowledge Graphs — SPARQL queries translate into the joins of this topic