parent
411908dcee
commit
973d422a49
3 changed files with 307 additions and 2 deletions
@ -0,0 +1,180 @@ |
|||||||
|
--- |
||||||
|
title: Foreign Key Constraint |
||||||
|
description: Learn how foreign keys are used to enforce relationships between tables |
||||||
|
order: 130 |
||||||
|
type: lesson-challenge |
||||||
|
--- |
||||||
|
|
||||||
|
In our previous lessons, we learned about relationships between tables and how they are achieved using primary and foreign keys. |
||||||
|
|
||||||
|
Now, let's explore how to enforce these relationships using foreign key constraints. |
||||||
|
|
||||||
|
## What is a Foreign Key Constraint? |
||||||
|
|
||||||
|
A foreign key constraint is a rule that ensures referential integrity between two tables. It prevents actions that would destroy relationships between tables or create invalid references. |
||||||
|
|
||||||
|
### What is referential integrity? |
||||||
|
|
||||||
|
Referential integrity is the idea that data |
||||||
|
|
||||||
|
Let's use our bookstore example to understand foreign key constraints: |
||||||
|
|
||||||
|
```sql |
||||||
|
CREATE TABLE books ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
title VARCHAR(255) NOT NULL, |
||||||
|
price DECIMAL(10, 2) |
||||||
|
); |
||||||
|
|
||||||
|
CREATE TABLE sales ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
book_id INTEGER, |
||||||
|
quantity INTEGER, |
||||||
|
sale_date DATE, |
||||||
|
FOREIGN KEY (book_id) REFERENCES books(id) |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
In this example: |
||||||
|
|
||||||
|
- The `book_id` column in the `sales` table is a foreign key |
||||||
|
- It references the `id` column in the `books` table |
||||||
|
- The foreign key constraint ensures that every `book_id` in `sales` must exist in the `books` table |
||||||
|
|
||||||
|
### Alternative Syntax |
||||||
|
|
||||||
|
Just like other constraints, you can also define foreign keys using different syntaxes: |
||||||
|
|
||||||
|
```sql |
||||||
|
-- Using CONSTRAINT keyword to name the foreign key |
||||||
|
CREATE TABLE sales ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
book_id INTEGER, |
||||||
|
quantity INTEGER, |
||||||
|
sale_date DATE, |
||||||
|
CONSTRAINT fk_book |
||||||
|
FOREIGN KEY (book_id) |
||||||
|
REFERENCES books(id) |
||||||
|
); |
||||||
|
|
||||||
|
-- Inline syntax |
||||||
|
CREATE TABLE sales ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
book_id INTEGER REFERENCES books(id), |
||||||
|
quantity INTEGER, |
||||||
|
sale_date DATE |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
## How Foreign Keys Maintain Data Integrity |
||||||
|
|
||||||
|
Foreign keys prevent several types of invalid operations: |
||||||
|
|
||||||
|
### 1. Inserting Invalid References |
||||||
|
|
||||||
|
```sql |
||||||
|
-- This will fail if book_id=999 doesn't exist in books table |
||||||
|
INSERT INTO sales (book_id, quantity, sale_date) |
||||||
|
VALUES (999, 1, '2024-03-20'); |
||||||
|
``` |
||||||
|
|
||||||
|
### 2. Deleting Referenced Records |
||||||
|
|
||||||
|
By default, you cannot delete a record from the parent table if it's referenced in the child table: |
||||||
|
|
||||||
|
```sql |
||||||
|
-- This will fail if any sales reference this book |
||||||
|
DELETE FROM books WHERE id = 1; |
||||||
|
``` |
||||||
|
|
||||||
|
### 3. Updating Referenced Keys |
||||||
|
|
||||||
|
Similarly, you cannot update a primary key if it's referenced by other tables: |
||||||
|
|
||||||
|
```sql |
||||||
|
-- This will fail if any sales reference this book |
||||||
|
UPDATE books SET id = 999 WHERE id = 1; |
||||||
|
``` |
||||||
|
|
||||||
|
## ON DELETE and ON UPDATE Clauses |
||||||
|
|
||||||
|
You can specify what happens when a referenced record is deleted or updated using `ON DELETE` and `ON UPDATE` clauses: |
||||||
|
|
||||||
|
```sql |
||||||
|
CREATE TABLE sales ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
book_id INTEGER, |
||||||
|
quantity INTEGER, |
||||||
|
sale_date DATE, |
||||||
|
FOREIGN KEY (book_id) |
||||||
|
REFERENCES books(id) |
||||||
|
ON DELETE CASCADE |
||||||
|
ON UPDATE CASCADE |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
Available options include: |
||||||
|
|
||||||
|
| Option | Description | |
||||||
|
| ----------- | --------------------------------------------------- | |
||||||
|
| CASCADE | Automatically delete/update related records | |
||||||
|
| SET NULL | Set the foreign key to NULL | |
||||||
|
| SET DEFAULT | Set the foreign key to its default value | |
||||||
|
| RESTRICT | Prevent the deletion/update (this is the default) | |
||||||
|
| NO ACTION | Similar to RESTRICT, but checked at different times | |
||||||
|
|
||||||
|
### Example with Multiple Options |
||||||
|
|
||||||
|
```sql |
||||||
|
CREATE TABLE books ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
author_id INTEGER, |
||||||
|
title VARCHAR(255), |
||||||
|
FOREIGN KEY (author_id) |
||||||
|
REFERENCES authors(id) |
||||||
|
ON DELETE SET NULL -- If author is deleted, set author_id to NULL |
||||||
|
ON UPDATE CASCADE -- If author_id changes, update it here too |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
## Composite Foreign Keys |
||||||
|
|
||||||
|
Just like primary keys, foreign keys can also consist of multiple columns: |
||||||
|
|
||||||
|
```sql |
||||||
|
CREATE TABLE book_editions ( |
||||||
|
book_id INTEGER, |
||||||
|
edition_number INTEGER, |
||||||
|
publisher_id INTEGER, |
||||||
|
publication_year INTEGER, |
||||||
|
PRIMARY KEY (book_id, edition_number), |
||||||
|
FOREIGN KEY (book_id, edition_number) |
||||||
|
REFERENCES books(id, edition) |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
## Best Practices |
||||||
|
|
||||||
|
1. **Always Name Your Constraints**: This makes error messages more meaningful and maintenance easier: |
||||||
|
|
||||||
|
```sql |
||||||
|
CONSTRAINT fk_book_author |
||||||
|
FOREIGN KEY (author_id) |
||||||
|
REFERENCES authors(id) |
||||||
|
``` |
||||||
|
|
||||||
|
2. **Consider Indexing**: Foreign key columns are often used in JOIN operations, so consider adding indexes: |
||||||
|
|
||||||
|
```sql |
||||||
|
CREATE INDEX idx_book_author ON books(author_id); |
||||||
|
``` |
||||||
|
|
||||||
|
3. **Choose ON DELETE/UPDATE Actions Carefully**: |
||||||
|
|
||||||
|
- Use `CASCADE` when child records cannot exist without parent |
||||||
|
- Use `SET NULL` when child records can exist independently |
||||||
|
- Use `RESTRICT` when you want to prevent accidental deletions |
||||||
|
|
||||||
|
4. **Maintain Data Consistency**: Always insert parent records before child records and remove child records before parent records. |
||||||
|
|
||||||
|
In the next lesson, we'll learn how to query data across multiple tables using these relationships. |
@ -0,0 +1,125 @@ |
|||||||
|
--- |
||||||
|
title: JOINs in Queries |
||||||
|
description: Learn how to query data across multiple tables using joins |
||||||
|
order: 120 |
||||||
|
type: lesson-challenge |
||||||
|
setup: | |
||||||
|
```sql |
||||||
|
CREATE TABLE author ( |
||||||
|
id INT PRIMARY KEY, |
||||||
|
name VARCHAR(255) |
||||||
|
); |
||||||
|
|
||||||
|
CREATE TABLE author_biography ( |
||||||
|
id INT PRIMARY KEY, |
||||||
|
author_id INT, |
||||||
|
biography TEXT |
||||||
|
); |
||||||
|
|
||||||
|
INSERT INTO author (id, name) |
||||||
|
VALUES (1, 'John Doe'), |
||||||
|
(2, 'Jane Smith'), |
||||||
|
(3, 'Alice Johnson'), |
||||||
|
(4, 'Bob Brown'); |
||||||
|
|
||||||
|
INSERT INTO author_biography (id, author_id, biography) |
||||||
|
VALUES (1, 4, 'Bob Brown\'s biography'), |
||||||
|
(2, 3, 'Alice Johnson\'s biography'), |
||||||
|
(3, 2, 'Jane Smith\'s biography'), |
||||||
|
(4, 1, 'John Doe\'s biography'); |
||||||
|
``` |
||||||
|
--- |
||||||
|
|
||||||
|
So far in this course, we have been mainly writing single-table queries. However, our previous lesson should have given you an idea of how data might not always be stored in a single table. |
||||||
|
|
||||||
|
In this lesson, we will look at how to query data from multiple tables. |
||||||
|
|
||||||
|
## What is a JOIN? |
||||||
|
|
||||||
|
JOINs help us query data from multiple tables based on a related column between them. The result of a JOIN is a new table with related data and columns from all the tables that were joined. |
||||||
|
|
||||||
|
Let's take an example to understand this better. Given the following two tables `author` and `author_biography`: |
||||||
|
|
||||||
|
[![](https://assets.roadmap.sh/guest/author-biography-8evoz.png)](https://assets.roadmap.sh/guest/author-biography-8evoz.png) |
||||||
|
|
||||||
|
Notice how `author_id` in the `author_biography` helps us relate each biography to an author. We can use this relationship to join the two tables and get each author along with their biography. |
||||||
|
|
||||||
|
To do this, we can use the `JOIN` clause. |
||||||
|
|
||||||
|
```sql |
||||||
|
SELECT * |
||||||
|
FROM author |
||||||
|
JOIN author_biography ON author.id = author_biography.author_id; |
||||||
|
-- ---------^------ ------------------^-------------------- |
||||||
|
-- Table to join with Join condition to find matching rows |
||||||
|
``` |
||||||
|
|
||||||
|
This query will return the following result set: |
||||||
|
|
||||||
|
[![](https://assets.roadmap.sh/guest/author-join-biography-r5jtd.png)](https://assets.roadmap.sh/guest/author-join-biography-r5jtd.png) |
||||||
|
|
||||||
|
Notice how the relevant biographies from `author_biography` are placed next to the relevant rows from the `author` table. |
||||||
|
|
||||||
|
> I would encourage you to try this query out in the editor and see how it works. |
||||||
|
|
||||||
|
### Selecting Specific Columns |
||||||
|
|
||||||
|
If we only want to get the author id, name and biography, we can do so by selecting the relevant columns from the joined table. |
||||||
|
|
||||||
|
```sql |
||||||
|
SELECT author.id, author.name, author_biography.biography |
||||||
|
FROM author |
||||||
|
JOIN author_biography ON author.id = author_biography.author_id; |
||||||
|
``` |
||||||
|
|
||||||
|
The output from this query will be: |
||||||
|
|
||||||
|
| id | name | biography | |
||||||
|
| --- | ------------- | ------------------------- | |
||||||
|
| 4 | Bob Brown | Bob Brown's biography | |
||||||
|
| 3 | Alice Johnson | Alice Johnson's biography | |
||||||
|
| 2 | Jane Smith | Jane Smith's biography | |
||||||
|
| 1 | John Doe | John Doe's biography | |
||||||
|
|
||||||
|
The way this query works as follows: |
||||||
|
|
||||||
|
**Step 1** `author` and `author_biography` are joined by verifying the `ON` condition i.e. `author.id = author_biography.author_id`. |
||||||
|
|
||||||
|
![](https://assets.roadmap.sh/guest/step1-joining-tables-t9ms5.png) |
||||||
|
|
||||||
|
**Step 2** Intermediate result is created with data from both tables. |
||||||
|
|
||||||
|
![](https://assets.roadmap.sh/guest/step2-joined-tables-hf315.png) |
||||||
|
|
||||||
|
**Step 3** Columns are returned based on `SELECT` clause. |
||||||
|
|
||||||
|
![](https://assets.roadmap.sh/guest/step3-result-zhqzl.png) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Let's take the same example our bookstore from our previous lesson. Among the tables we had in our bookstore, we had `customer` and `sale`. Here is some data we have in both tables respectively: |
||||||
|
|
||||||
|
| id | name | email | phone | |
||||||
|
| --- | ------------- | ------------------------- | ---------- | |
||||||
|
| 1 | John Doe | john.doe@example.com | 1234567890 | |
||||||
|
| 2 | Jane Smith | jane.smith@example.com | 0987654321 | |
||||||
|
| 3 | Alice Johnson | alice.johnson@example.com | 1122334455 | |
||||||
|
| 4 | Bob Brown | bob.brown@example.com | 9988776655 | |
||||||
|
| 5 | Charlie Davis | charlie.davis@example.com | 1231231230 | |
||||||
|
|
||||||
|
| id | customer_id | book_id | quantity | price | date | |
||||||
|
| --- | ----------- | ------- | -------- | ------ | ---------- | |
||||||
|
| 1 | 1 | 1 | 2 | 20.00 | 2024-01-01 | |
||||||
|
| 2 | 2 | 2 | 3 | 45.00 | 2024-01-02 | |
||||||
|
| 3 | 2 | 1 | 1 | 10.00 | 2024-02-03 | |
||||||
|
| 4 | 1 | 1 | 1 | 10.00 | 2024-02-04 | |
||||||
|
| 5 | 1 | 1 | 2 | 20.00 | 2024-02-05 | |
||||||
|
| 6 | 5 | 3 | 1 | 30.00 | 2024-02-06 | |
||||||
|
| 7 | 4 | 3 | 4 | 120.00 | 2024-02-07 | |
||||||
|
| 8 | 3 | 4 | 1 | 25.00 | 2024-02-08 | |
||||||
|
| 9 | 3 | 4 | 1 | 25.00 | 2024-02-09 | |
||||||
|
| 10 | 5 | 4 | 2 | 50.00 | 2024-02-10 | |
||||||
|
|
||||||
|
Notice how the `sale` table has a `customer_id` column that references the `id` column in the `customer` table. This is a relationship between the two tables. |
||||||
|
|
||||||
|
We can use this relationship to query data from both tables. |
Loading…
Reference in new issue