- No previous SQL experience is required
- Basic familiarity with tables or spreadsheet-style rows and columns is helpful
SQL & Relational Databases
Learn how relational databases model data, how SQL retrieves and changes it, and how indexes and transactions shape correctness and performance.
Know where you are going before you begin
A useful course should make the starting point, destination, and learning method clear—not make you guess from a list of links.
Inside this course
Move through the material in order unless a prerequisite or practice link gives you a better reason to branch.
Turn real-world entities and relationships into tables, primary keys, foreign keys, and constraints.
Build queries by reasoning about row sets, join cardinality, grouping, and the order of logical operations.
A concise reference for B-tree indexes, selectivity, ACID properties, isolation, locking, and common performance traps.
Continue with purpose
These are curated relationships from the SubjectVision learning graph. Each recommendation should answer why it is useful next rather than merely adding another link.
Course interview practice
Use these as short database reasoning drills after the course. Describe the rows you expect before writing SQL, state the integrity or performance property you are protecting, and explain the trade-off instead of reciting syntax.
01 · IntermediateTwo customers can share the same name. How would you model orders so a customer can change their name without rewriting historical order rows?Answer out loud before opening the expected response.
Practice
Answer out loud before opening the expected response.
Expected answer
Give each customer a stable primary key such as customer_id, store that key as a foreign key on orders, and keep mutable customer attributes on the customer row instead of copying identity fields into every order.
Why this answer works
The important idea is identity versus description. A name is a mutable attribute and is not a reliable identifier. The foreign key gives each order a durable relationship to exactly one customer while avoiding repeated customer data. A strong answer can also mention constraints, cascade behavior, and when deliberately denormalized historical snapshots may still be appropriate.
02 · IntermediateA LEFT JOIN is supposed to keep customers with no orders, but adding a condition on the orders table removes those customers. What likely happened?Answer out loud before opening the expected response.
Practice
Answer out loud before opening the expected response.
Expected answer
A predicate on the right-side table was probably placed in WHERE, so rows with no match had NULL right-side values and were filtered out. Put the match-specific condition in the JOIN condition when unmatched left rows must remain.
Why this answer works
This is a row-set reasoning problem, not a keyword trick. LEFT JOIN first preserves unmatched left rows by filling right-side columns with NULL. A later WHERE predicate such as orders.status = paid rejects those NULL rows, making the result behave like an inner join for that condition. Explain the intended population before deciding whether the predicate belongs in ON or WHERE.
03 · IntermediateYou need departments whose average salary exceeds a threshold. Why is HAVING usually the correct place for that condition instead of WHERE?Answer out loud before opening the expected response.
Practice
Answer out loud before opening the expected response.
Expected answer
WHERE filters input rows before grouping, while HAVING filters the groups after aggregate values such as AVG(salary) have been computed. The aggregate condition therefore belongs after grouping.
Why this answer works
A strong SQL answer explains logical processing order. First decide which individual rows are eligible, then form groups, then calculate aggregates, and only then decide which aggregate results survive. Mixing those stages causes both syntax errors and subtler business-logic mistakes, especially when a row-level filter changes which rows contribute to the average.
04 · SeniorA query filters a large table by a low-selectivity status column, yet the database chooses a table scan instead of the status index. Why can that be reasonable?Answer out loud before opening the expected response.
Practice
Answer out loud before opening the expected response.
Expected answer
If the predicate matches a large fraction of the table, using the index may require many lookups and random reads, so scanning the table can cost less. Index usefulness depends on selectivity, access pattern, covering columns, table size, and optimizer estimates.
Why this answer works
Indexes are not automatically faster. They add an alternate access path, and the optimizer compares its estimated cost with other plans. When many rows match, repeatedly walking an index and then fetching table pages can cost more than reading the table sequentially. A strong answer also mentions stale statistics, composite-index order, covering indexes, and measuring the actual execution plan.
05 · SeniorTwo checkout requests read the last inventory item at the same time and both try to sell it. What database property must the design protect?Answer out loud before opening the expected response.
Practice
Answer out loud before opening the expected response.
Expected answer
The design must make the stock transition atomic under concurrency so both requests cannot commit a sale based on the same stale quantity. Use an appropriate transaction or conditional update and verify the affected-row result before confirming the order.
Why this answer works
The bug is a concurrency invariant violation: inventory must never fall below the allowed quantity or be promised twice. Merely starting a transaction is not enough if both transactions can still read stale state and later overwrite it. Strong answers discuss conditional updates, locking or isolation choices, retry behavior, deadlocks, and keeping the transaction scope small around the invariant being protected.
Test what you can apply
Five course-specific questions covering the most important ideas in SQL & Relational Databases. Commit to an answer before reading the explanation.
What is the most useful first question when designing a relational table?
Defining the grain—what one row means—prevents mixed facts and makes keys, constraints, and later queries easier to reason about.
Review this topic →Why is a foreign-key constraint useful?
A foreign key protects referential integrity by requiring referenced parent values to exist, subject to the configured update/delete behavior.
Review this topic →Which clause should usually filter groups based on COUNT(*)?
HAVING applies conditions after grouping, so it can filter on aggregate results such as COUNT or SUM.
Review this topic →Why can putting a right-table condition in WHERE change a LEFT JOIN?
Rows with no right-side match contain NULLs; a WHERE predicate on that right-side column often rejects them, defeating the outer-join intent.
Review this topic →Which statement best describes database indexes?
Indexes trade extra storage and maintenance work for cheaper access paths on matching predicates, joins, or orderings.
Review this topic →Review any missed answers, then continue while the concepts are fresh enough to connect.