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