Database Relationships, Keys, and Joins: Complete Guide

Table of Contents

  1. Database Keys
  2. Database Relationships
  3. SQL Joins
  4. Practical Examples
  5. Quick Reference
  6. Best Practices

Database Keys

Think of keys as unique identifiers or labels that help you find and organize data in your database. Like how your house has a unique address, or you have a unique social security number.


Primary Key

What is it?

A Primary Key (PK) is a column (or combination of columns) that uniquely identifies each row in a table.

Rules:

Think of it as:

Your student ID card number - no two students have the same ID, and everyone must have one.

Example:

CREATE TABLE Students (
    StudentID INT PRIMARY KEY,          -- This is the Primary Key
    FirstName VARCHAR(50),
    LastName VARCHAR(50),
    Email VARCHAR(100)
);

Sample Data:

StudentID (PK) FirstName LastName Email
1 John Smith john@email.com
2 Sarah Johnson sarah@email.com
3 Mike Davis mike@email.com

When to Use:

Common Patterns:


Foreign Key

What is it?

A Foreign Key (FK) is a column that references the Primary Key of another table. It creates a relationship between two tables.

Rules:

Think of it as:

Your library card number that links you to the library’s patron database. The card references YOU in another table.

Example:

CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    OrderDate DATE,
    CustomerID INT,                     -- This is a Foreign Key
    TotalAmount DECIMAL(10, 2),
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

CREATE TABLE Customers (
    CustomerID INT PRIMARY KEY,         -- Referenced by Orders table
    CustomerName VARCHAR(100),
    Email VARCHAR(100)
);

Sample Data:

Customers Table: | CustomerID (PK) | CustomerName | Email | |—————–|————–|——-| | 1 | Alice Brown | alice@email.com | | 2 | Bob Wilson | bob@email.com |

Orders Table: | OrderID (PK) | OrderDate | CustomerID (FK) | TotalAmount | |————–|———–|—————–|————-| | 101 | 2024-01-15 | 1 | 150.00 | | 102 | 2024-01-16 | 1 | 200.00 | | 103 | 2024-01-17 | 2 | 75.50 |

Notice:

When to Use:

What Foreign Keys Prevent:

-- ❌ This will FAIL - CustomerID 999 doesn't exist in Customers table
INSERT INTO Orders VALUES (104, '2024-01-18', 999, 50.00);

-- ❌ This will FAIL - Can't delete a customer who has orders
DELETE FROM Customers WHERE CustomerID = 1;

-- ✅ This works - Customer 1 exists
INSERT INTO Orders VALUES (104, '2024-01-18', 1, 50.00);

Composite Key

What is it?

A Composite Key is a Primary Key made up of TWO OR MORE columns together.

Think of it as:

Your seat at a concert: Row + Seat Number together identify your unique seat. Row alone isn’t unique, Seat Number alone isn’t unique, but together they are!

Example:

CREATE TABLE CourseEnrollments (
    StudentID INT,
    CourseID INT,
    Semester VARCHAR(20),
    Grade VARCHAR(2),
    PRIMARY KEY (StudentID, CourseID, Semester)  -- Composite Key
);

Sample Data:

StudentID CourseID Semester Grade
1 101 Spring 2024 A
1 102 Spring 2024 B+
1 101 Fall 2024 A-
2 101 Spring 2024 B

Why Composite?

When to Use:

Alternative Approach:

Instead of a composite key, you can use a surrogate key:

CREATE TABLE CourseEnrollments (
    EnrollmentID INT PRIMARY KEY AUTO_INCREMENT,  -- Surrogate Key
    StudentID INT,
    CourseID INT,
    Semester VARCHAR(20),
    Grade VARCHAR(2),
    UNIQUE (StudentID, CourseID, Semester)        -- Still enforce uniqueness
);

Candidate Key

What is it?

A Candidate Key is any column (or combination of columns) that COULD be used as a Primary Key. They are “candidates” for being the primary key.

Think of it as:

Ways to uniquely identify a person: Social Security Number, Passport Number, Driver’s License Number. Each could be the primary key - they’re all candidates!

Example:

CREATE TABLE Employees (
    EmployeeID INT,           -- Candidate Key 1
    SSN VARCHAR(11),          -- Candidate Key 2
    Email VARCHAR(100),       -- Candidate Key 3
    EmployeeName VARCHAR(100),
    -- One of these will be chosen as PRIMARY KEY
    PRIMARY KEY (EmployeeID)
);

All Candidate Keys:

  1. EmployeeID - unique for each employee
  2. SSN - unique for each person
  3. Email - unique in the company

Only ONE becomes the Primary Key, the others become Alternate Keys.

When to Identify:


Alternate Key

What is it?

An Alternate Key is a Candidate Key that was NOT chosen as the Primary Key.

Think of it as:

If you use your passport number as your primary ID, your driver’s license number becomes an alternate way to identify you.

Example:

CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY,     -- This is the Primary Key
    SSN VARCHAR(11) UNIQUE,         -- Alternate Key
    Email VARCHAR(100) UNIQUE,      -- Alternate Key
    EmployeeName VARCHAR(100)
);

When to Use:


Surrogate Key

What is it?

A Surrogate Key is an artificial key created by the database (usually auto-increment) that has no business meaning.

Think of it as:

An order number that’s just generated sequentially (1, 2, 3…) rather than using something meaningful like customer name + date.

Example:

CREATE TABLE Products (
    ProductID INT PRIMARY KEY AUTO_INCREMENT,  -- Surrogate Key (no real-world meaning)
    ProductName VARCHAR(100),
    SKU VARCHAR(50),                           -- Natural Key (has meaning)
    Price DECIMAL(10, 2)
);

Sample Data:

ProductID (Surrogate) ProductName SKU (Natural) Price
1 Laptop TECH-LP-001 999.99
2 Mouse TECH-MS-002 29.99
3 Keyboard TECH-KB-003 79.99

Advantages:

When to Use:


Natural Key

What is it?

A Natural Key is a key that has real-world meaning and naturally exists in your data.

Think of it as:

Using someone’s email address or Social Security Number as their ID - it already exists and has meaning.

Example:

CREATE TABLE Countries (
    CountryCode CHAR(2) PRIMARY KEY,  -- Natural Key (ISO country code)
    CountryName VARCHAR(100),
    Population INT
);

Sample Data:

CountryCode (Natural PK) CountryName Population
US United States 331000000
UK United Kingdom 67000000
CA Canada 38000000

Other Examples of Natural Keys:

Disadvantages:

When to Use:


Unique Key

What is it?

A Unique Key constraint ensures that all values in a column are different from each other.

Difference from Primary Key:

Example:

CREATE TABLE Users (
    UserID INT PRIMARY KEY,
    Username VARCHAR(50) UNIQUE,      -- Must be unique
    Email VARCHAR(100) UNIQUE,        -- Must be unique
    PhoneNumber VARCHAR(15) UNIQUE    -- Must be unique
);

Sample Data:

UserID (PK) Username (UNIQUE) Email (UNIQUE) PhoneNumber (UNIQUE)
1 john_smith john@email.com 555-1234
2 sarah_j sarah@email.com 555-5678
3 mike_d mike@email.com 555-9012

What Happens:

-- ✅ This works
INSERT INTO Users VALUES (4, 'alice_w', 'alice@email.com', '555-1111');

-- ❌ This FAILS - Username 'john_smith' already exists
INSERT INTO Users VALUES (5, 'john_smith', 'newemail@email.com', '555-2222');

-- ❌ This FAILS - Email already exists
INSERT INTO Users VALUES (5, 'bob_m', 'john@email.com', '555-3333');

When to Use:


Super Key

What is it?

A Super Key is any combination of columns that uniquely identifies a row. It might include extra, unnecessary columns.

Think of it as:

Using your (Name + SSN + Birthday + Address) to identify you. It’s unique, but SSN alone would work - the rest is extra!

Example:

For this table: | StudentID | Email | FirstName | LastName | |———–|——-|———–|———-| | 1 | john@email.com | John | Smith |

Super Keys:

Candidate Keys are Super Keys with NO extra columns!

When to Care:


Database Relationships

Relationships define how tables are connected to each other. Think of them as the “glue” that holds your database together!


One-to-One (1:1)

What is it?

One record in Table A is related to exactly one record in Table B, and vice versa.

Think of it as:

Visual Representation:

Person ←──1:1──→ Passport

When to Use:

  1. Sensitive Information Separation: Keep sensitive data in a separate table
  2. Performance: Split large tables with rarely-used columns
  3. Optional Attributes: When some records have extra information others don’t

Example 1: User Authentication

-- Main user information
CREATE TABLE Users (
    UserID INT PRIMARY KEY AUTO_INCREMENT,
    Username VARCHAR(50) UNIQUE,
    Email VARCHAR(100),
    CreatedAt DATETIME
);

-- Sensitive authentication data (separate for security)
CREATE TABLE UserPasswords (
    UserID INT PRIMARY KEY,              -- Same as Users.UserID
    PasswordHash VARCHAR(255),
    LastPasswordChange DATETIME,
    FOREIGN KEY (UserID) REFERENCES Users(UserID)
);

Sample Data:

Users Table: | UserID (PK) | Username | Email | CreatedAt | |————-|———-|——-|———–| | 1 | john_doe | john@email.com | 2024-01-01 | | 2 | sarah_m | sarah@email.com | 2024-01-02 |

UserPasswords Table: | UserID (PK, FK) | PasswordHash | LastPasswordChange | |—————–|————–|——————-| | 1 | $2y$10$abc… | 2024-01-15 | | 2 | $2y$10$xyz… | 2024-01-20 |

Notice: Each user has exactly ONE password record, and each password belongs to exactly ONE user.

Example 2: Employee Details

CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY,
    FirstName VARCHAR(50),
    LastName VARCHAR(50),
    Department VARCHAR(50)
);

CREATE TABLE EmployeeDetails (
    EmployeeID INT PRIMARY KEY,
    EmergencyContact VARCHAR(100),
    MedicalInfo TEXT,
    FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID)
);

Why Separate?

Implementation Options:

Option 1: Foreign Key in Either Table

-- FK in Table B pointing to Table A
CREATE TABLE TableB (
    ID INT PRIMARY KEY,
    TableA_ID INT UNIQUE,               -- UNIQUE ensures 1:1
    FOREIGN KEY (TableA_ID) REFERENCES TableA(ID)
);

Option 2: Shared Primary Key (Recommended)

-- Both tables share the same PK
CREATE TABLE TableB (
    ID INT PRIMARY KEY,                 -- Same as TableA's ID
    FOREIGN KEY (ID) REFERENCES TableA(ID)
);

One-to-Many (1:N)

What is it?

One record in Table A can be related to many records in Table B, but each record in Table B is related to only one record in Table A.

Think of it as:

Visual Representation:

Customer 1──→ many Orders

When to Use:

Example 1: Customers and Orders

CREATE TABLE Customers (
    CustomerID INT PRIMARY KEY AUTO_INCREMENT,
    CustomerName VARCHAR(100),
    Email VARCHAR(100)
);

CREATE TABLE Orders (
    OrderID INT PRIMARY KEY AUTO_INCREMENT,
    CustomerID INT,                      -- Foreign Key
    OrderDate DATE,
    TotalAmount DECIMAL(10, 2),
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

Sample Data:

Customers Table: | CustomerID (PK) | CustomerName | Email | |—————–|————–|——-| | 1 | Alice Brown | alice@email.com | | 2 | Bob Wilson | bob@email.com |

Orders Table: | OrderID (PK) | CustomerID (FK) | OrderDate | TotalAmount | |————–|—————–|———–|————-| | 101 | 1 | 2024-01-15 | 150.00 | | 102 | 1 | 2024-01-20 | 200.00 | | 103 | 1 | 2024-02-01 | 75.00 | | 104 | 2 | 2024-01-18 | 300.00 |

Notice:

Example 2: Blog Posts and Comments

CREATE TABLE BlogPosts (
    PostID INT PRIMARY KEY AUTO_INCREMENT,
    Title VARCHAR(200),
    Content TEXT,
    AuthorID INT,
    PublishedDate DATETIME
);

CREATE TABLE Comments (
    CommentID INT PRIMARY KEY AUTO_INCREMENT,
    PostID INT,                          -- Foreign Key
    CommenterName VARCHAR(100),
    CommentText TEXT,
    CommentDate DATETIME,
    FOREIGN KEY (PostID) REFERENCES BlogPosts(PostID)
);

Sample Data:

BlogPosts Table: | PostID (PK) | Title | AuthorID | PublishedDate | |————-|——-|———-|—————| | 1 | Introduction to SQL | 5 | 2024-01-10 | | 2 | Advanced Joins | 5 | 2024-01-15 |

Comments Table: | CommentID (PK) | PostID (FK) | CommenterName | CommentText | CommentDate | |—————-|————-|—————|————-|————-| | 1 | 1 | John | Great post! | 2024-01-11 | | 2 | 1 | Sarah | Very helpful | 2024-01-11 | | 3 | 1 | Mike | Thanks! | 2024-01-12 | | 4 | 2 | Lisa | Excellent | 2024-01-16 |

Notice: Post 1 has 3 comments, Post 2 has 1 comment.

Key Point:

The Foreign Key ALWAYS goes in the “Many” side!


Many-to-Many (M:N)

What is it?

Many records in Table A can relate to many records in Table B, and vice versa.

Think of it as:

Visual Representation:

Students ←── many:many ──→ Courses

IMPORTANT: You cannot directly implement many-to-many in SQL!

Solution: Junction Table (Associative Entity)

A junction table sits between the two tables and breaks the M:N into TWO one-to-many relationships!

Students 1──→ many StudentCourses many ←──1 Courses

Example 1: Students and Courses

CREATE TABLE Students (
    StudentID INT PRIMARY KEY AUTO_INCREMENT,
    StudentName VARCHAR(100),
    Email VARCHAR(100)
);

CREATE TABLE Courses (
    CourseID INT PRIMARY KEY AUTO_INCREMENT,
    CourseName VARCHAR(100),
    Credits INT
);

-- Junction Table (also called Bridge Table, Linking Table)
CREATE TABLE StudentCourses (
    StudentID INT,
    CourseID INT,
    EnrollmentDate DATE,
    Grade VARCHAR(2),
    PRIMARY KEY (StudentID, CourseID),   -- Composite Primary Key
    FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
    FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);

Sample Data:

Students Table: | StudentID (PK) | StudentName | Email | |—————-|————-|——-| | 1 | Alice | alice@email.com | | 2 | Bob | bob@email.com | | 3 | Charlie | charlie@email.com |

Courses Table: | CourseID (PK) | CourseName | Credits | |—————|————|———| | 101 | Mathematics | 3 | | 102 | Physics | 4 | | 103 | Chemistry | 4 |

StudentCourses Table (Junction): | StudentID (PK, FK) | CourseID (PK, FK) | EnrollmentDate | Grade | |——————–|——————-|—————-|——-| | 1 | 101 | 2024-01-15 | A | | 1 | 102 | 2024-01-15 | B+ | | 1 | 103 | 2024-01-15 | A- | | 2 | 101 | 2024-01-16 | B | | 2 | 102 | 2024-01-16 | B+ | | 3 | 103 | 2024-01-17 | A |

Notice:

Example 2: Books and Authors

CREATE TABLE Authors (
    AuthorID INT PRIMARY KEY AUTO_INCREMENT,
    AuthorName VARCHAR(100),
    Country VARCHAR(50)
);

CREATE TABLE Books (
    BookID INT PRIMARY KEY AUTO_INCREMENT,
    BookTitle VARCHAR(200),
    ISBN VARCHAR(13),
    PublishedYear INT
);

-- Junction Table
CREATE TABLE BookAuthors (
    BookID INT,
    AuthorID INT,
    AuthorOrder INT,                     -- Additional info: 1st author, 2nd author, etc.
    PRIMARY KEY (BookID, AuthorID),
    FOREIGN KEY (BookID) REFERENCES Books(BookID),
    FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID)
);

Sample Data:

Authors Table: | AuthorID (PK) | AuthorName | Country | |—————|————|———| | 1 | J.K. Rowling | UK | | 2 | Stephen King | USA | | 3 | Peter Straub | USA |

Books Table: | BookID (PK) | BookTitle | ISBN | PublishedYear | |————-|———–|——|—————| | 1 | Harry Potter 1 | 9780439708180 | 1997 | | 2 | Harry Potter 2 | 9780439064873 | 1998 | | 3 | The Talisman | 9780345444882 | 1984 | | 4 | The Shining | 9780307743657 | 1977 |

BookAuthors Table (Junction): | BookID (FK) | AuthorID (FK) | AuthorOrder | |————-|—————|————-| | 1 | 1 | 1 | | 2 | 1 | 1 | | 3 | 2 | 1 | | 3 | 3 | 2 | | 4 | 2 | 1 |

Notice:

Junction Table Can Store Additional Information:


Self-Referencing Relationship

What is it?

A table that has a relationship with itself. A record references another record in the same table.

Think of it as:

Visual Representation:

Employee ──→ Manager (who is also an Employee)

Example 1: Employee-Manager Hierarchy

CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY AUTO_INCREMENT,
    EmployeeName VARCHAR(100),
    JobTitle VARCHAR(50),
    ManagerID INT,                       -- Self-referencing Foreign Key
    FOREIGN KEY (ManagerID) REFERENCES Employees(EmployeeID)
);

Sample Data:

EmployeeID (PK) EmployeeName JobTitle ManagerID (FK)
1 Alice Johnson CEO NULL
2 Bob Smith VP Sales 1
3 Carol White VP Engineering 1
4 David Brown Sales Manager 2
5 Eve Davis Developer 3
6 Frank Wilson Developer 3

Hierarchy:

Alice (CEO)
├── Bob (VP Sales)
│   └── David (Sales Manager)
└── Carol (VP Engineering)
    ├── Eve (Developer)
    └── Frank (Developer)

Notice:

Example 2: Category Hierarchy

CREATE TABLE Categories (
    CategoryID INT PRIMARY KEY AUTO_INCREMENT,
    CategoryName VARCHAR(100),
    ParentCategoryID INT,                -- Self-referencing FK
    FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID)
);

Sample Data:

CategoryID (PK) CategoryName ParentCategoryID (FK)
1 Electronics NULL
2 Computers 1
3 Laptops 2
4 Desktops 2
5 Phones 1
6 Smartphones 5
7 Feature Phones 5

Hierarchy:

Electronics
├── Computers
│   ├── Laptops
│   └── Desktops
└── Phones
    ├── Smartphones
    └── Feature Phones

When to Use:


SQL Joins

Joins are used to combine data from multiple tables based on related columns. Think of joins as bringing together information that’s stored in different places!


Visual Guide to Joins

Imagine two tables as circles (Venn diagram):

Table A          Table B
   ●●●●●●●     ●●●●●●●
  ●●●●●●●●●   ●●●●●●●●●
  ●●●●●●●●●●●●●●●●●●●●
  ●●●●●●●●●   ●●●●●●●●●
   ●●●●●●●     ●●●●●●●

Let’s use these sample tables for all examples:

Sample Tables:

Customers Table: | CustomerID (PK) | CustomerName | City | |—————–|————–|——| | 1 | Alice | New York | | 2 | Bob | Los Angeles | | 3 | Charlie | Chicago | | 4 | Diana | Houston |

Orders Table: | OrderID (PK) | CustomerID (FK) | OrderDate | Amount | |————–|—————–|———–|——–| | 101 | 1 | 2024-01-15 | 150.00 | | 102 | 1 | 2024-01-20 | 200.00 | | 103 | 2 | 2024-01-18 | 300.00 | | 104 | 5 | 2024-01-25 | 100.00 |

Notice:


INNER JOIN

What is it?

Returns only the rows where there’s a match in BOTH tables.

Visual:

Table A          Table B
   ●●●●●●●     ●●●●●●●
  ●●●●●●●●●   ●●●●●●●●●
  ●●●●[XXXXX]●●●●●●●●●  ← Only this overlapping part
  ●●●●●●●●●   ●●●●●●●●●
   ●●●●●●●     ●●●●●●●

Syntax:

SELECT columns
FROM Table1
INNER JOIN Table2
ON Table1.column = Table2.column;

Example:

SELECT 
    Customers.CustomerName,
    Customers.City,
    Orders.OrderID,
    Orders.OrderDate,
    Orders.Amount
FROM Customers
INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;

Result:

CustomerName City OrderID OrderDate Amount
Alice New York 101 2024-01-15 150.00
Alice New York 102 2024-01-20 200.00
Bob Los Angeles 103 2024-01-18 300.00

What Happened:

When to Use:

Alternative Syntax (older style - still works):

SELECT *
FROM Customers, Orders
WHERE Customers.CustomerID = Orders.CustomerID;

LEFT JOIN (LEFT OUTER JOIN)

What is it?

Returns ALL rows from the LEFT table, and matching rows from the right table. If no match, NULL values for right table columns.

Visual:

Table A          Table B
   [●●●●●●●]    ●●●●●●●
  [●●●●●●●●●]  ●●●●●●●●●
  [●●●●XXXXX]●●●●●●●●●   ← All of left + overlap
  [●●●●●●●●●]  ●●●●●●●●●
   [●●●●●●●]    ●●●●●●●

Syntax:

SELECT columns
FROM Table1
LEFT JOIN Table2
ON Table1.column = Table2.column;

Example:

SELECT 
    Customers.CustomerName,
    Customers.City,
    Orders.OrderID,
    Orders.OrderDate,
    Orders.Amount
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;

Result:

CustomerName City OrderID OrderDate Amount
Alice New York 101 2024-01-15 150.00
Alice New York 102 2024-01-20 200.00
Bob Los Angeles 103 2024-01-18 300.00
Charlie Chicago NULL NULL NULL
Diana Houston NULL NULL NULL

What Happened:

When to Use:

Find Customers Without Orders:

SELECT Customers.CustomerName
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderID IS NULL;

Result: | CustomerName | |————–| | Charlie | | Diana |


RIGHT JOIN (RIGHT OUTER JOIN)

What is it?

Returns ALL rows from the RIGHT table, and matching rows from the left table. If no match, NULL values for left table columns.

Visual:

Table A          Table B
   ●●●●●●●     [●●●●●●●]
  ●●●●●●●●●   [●●●●●●●●●]
  ●●●●[XXXXX]●●●●●●●●●]  ← All of right + overlap
  ●●●●●●●●●   [●●●●●●●●●]
   ●●●●●●●     [●●●●●●●]

Syntax:

SELECT columns
FROM Table1
RIGHT JOIN Table2
ON Table1.column = Table2.column;

Example:

SELECT 
    Customers.CustomerName,
    Customers.City,
    Orders.OrderID,
    Orders.OrderDate,
    Orders.Amount
FROM Customers
RIGHT JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;

Result:

CustomerName City OrderID OrderDate Amount
Alice New York 101 2024-01-15 150.00
Alice New York 102 2024-01-20 200.00
Bob Los Angeles 103 2024-01-18 300.00
NULL NULL 104 2024-01-25 100.00

What Happened:

When to Use:

RIGHT JOIN = LEFT JOIN with tables swapped:

-- These are equivalent:
SELECT * FROM A RIGHT JOIN B ON A.id = B.id;
SELECT * FROM B LEFT JOIN A ON B.id = A.id;

FULL OUTER JOIN

What is it?

Returns ALL rows from BOTH tables. Where there’s a match, combine them. Where no match, show NULL for the missing side.

Visual:

Table A          Table B
   [●●●●●●●]    [●●●●●●●]
  [●●●●●●●●●]  [●●●●●●●●●]
  [●●●●XXXXX●●●●●●●●●]  ← Everything from both
  [●●●●●●●●●]  [●●●●●●●●●]
   [●●●●●●●]    [●●●●●●●]

Syntax:

SELECT columns
FROM Table1
FULL OUTER JOIN Table2
ON Table1.column = Table2.column;

Example:

SELECT 
    Customers.CustomerName,
    Customers.City,
    Orders.OrderID,
    Orders.OrderDate,
    Orders.Amount
FROM Customers
FULL OUTER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;

Result:

CustomerName City OrderID OrderDate Amount
Alice New York 101 2024-01-15 150.00
Alice New York 102 2024-01-20 200.00
Bob Los Angeles 103 2024-01-18 300.00
Charlie Chicago NULL NULL NULL
Diana Houston NULL NULL NULL
NULL NULL 104 2024-01-25 100.00

What Happened:

When to Use:

MySQL Alternative (simulate FULL OUTER JOIN):

SELECT * FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID
UNION
SELECT * FROM Customers RIGHT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

CROSS JOIN

What is it?

Returns the Cartesian product - every row from Table A combined with every row from Table B.

Visual:

Table A (3 rows)  ×  Table B (4 rows)  =  12 result rows

Syntax:

SELECT columns
FROM Table1
CROSS JOIN Table2;

Example:

Sizes Table: | SizeID | SizeName | |——–|———-| | 1 | Small | | 2 | Medium | | 3 | Large |

Colors Table: | ColorID | ColorName | |———|———–| | 1 | Red | | 2 | Blue | | 3 | Green |

SELECT 
    Sizes.SizeName,
    Colors.ColorName
FROM Sizes
CROSS JOIN Colors;

Result (9 rows = 3 × 3):

SizeName ColorName
Small Red
Small Blue
Small Green
Medium Red
Medium Blue
Medium Green
Large Red
Large Blue
Large Green

What Happened:

When to Use:

Alternative Syntax:

-- These are equivalent:
SELECT * FROM A CROSS JOIN B;
SELECT * FROM A, B;

SELF JOIN

What is it?

A table joined with itself. Used to compare rows within the same table.

Syntax:

SELECT columns
FROM Table1 AS alias1
JOIN Table1 AS alias2
ON alias1.column = alias2.column;

Example 1: Employee-Manager Relationships

Employees Table: | EmployeeID | EmployeeName | ManagerID | |————|————–|———–| | 1 | Alice | NULL | | 2 | Bob | 1 | | 3 | Charlie | 1 | | 4 | David | 2 | | 5 | Eve | 2 |

SELECT 
    e.EmployeeName AS Employee,
    m.EmployeeName AS Manager
FROM Employees AS e
LEFT JOIN Employees AS m
ON e.ManagerID = m.EmployeeID;

Result:

Employee Manager
Alice NULL
Bob Alice
Charlie Alice
David Bob
Eve Bob

What Happened:

Example 2: Find Employees in Same Department

Employees Table: | EmployeeID | EmployeeName | Department | |————|————–|————| | 1 | Alice | Sales | | 2 | Bob | Sales | | 3 | Charlie | IT | | 4 | David | IT |

SELECT 
    e1.EmployeeName AS Employee1,
    e2.EmployeeName AS Employee2,
    e1.Department
FROM Employees AS e1
JOIN Employees AS e2
ON e1.Department = e2.Department
AND e1.EmployeeID < e2.EmployeeID;  -- Avoid duplicates

Result:

Employee1 Employee2 Department
Alice Bob Sales
Charlie David IT

What Happened:

When to Use:


Practical Examples

Example 1: E-commerce Database

Complete Schema:

CREATE TABLE Customers (
    CustomerID INT PRIMARY KEY AUTO_INCREMENT,
    CustomerName VARCHAR(100),
    Email VARCHAR(100) UNIQUE,
    City VARCHAR(50)
);

CREATE TABLE Products (
    ProductID INT PRIMARY KEY AUTO_INCREMENT,
    ProductName VARCHAR(100),
    Price DECIMAL(10, 2),
    Stock INT
);

CREATE TABLE Orders (
    OrderID INT PRIMARY KEY AUTO_INCREMENT,
    CustomerID INT,
    OrderDate DATE,
    TotalAmount DECIMAL(10, 2),
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

CREATE TABLE OrderItems (
    OrderItemID INT PRIMARY KEY AUTO_INCREMENT,
    OrderID INT,
    ProductID INT,
    Quantity INT,
    Price DECIMAL(10, 2),
    FOREIGN KEY (OrderID) REFERENCES Orders(OrderID),
    FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
);

Relationships:

Complex Query: Customer Order Details

SELECT 
    c.CustomerName,
    c.Email,
    o.OrderID,
    o.OrderDate,
    p.ProductName,
    oi.Quantity,
    oi.Price,
    (oi.Quantity * oi.Price) AS LineTotal
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
INNER JOIN OrderItems oi ON o.OrderID = oi.OrderID
INNER JOIN Products p ON oi.ProductID = p.ProductID
ORDER BY o.OrderDate DESC, o.OrderID;

This shows:


Example 2: School Database

Schema:

CREATE TABLE Students (
    StudentID INT PRIMARY KEY,
    StudentName VARCHAR(100),
    Email VARCHAR(100)
);

CREATE TABLE Courses (
    CourseID INT PRIMARY KEY,
    CourseName VARCHAR(100),
    Credits INT
);

CREATE TABLE Instructors (
    InstructorID INT PRIMARY KEY,
    InstructorName VARCHAR(100),
    Department VARCHAR(50)
);

CREATE TABLE CourseInstructors (
    CourseID INT,
    InstructorID INT,
    Semester VARCHAR(20),
    PRIMARY KEY (CourseID, InstructorID, Semester),
    FOREIGN KEY (CourseID) REFERENCES Courses(CourseID),
    FOREIGN KEY (InstructorID) REFERENCES Instructors(InstructorID)
);

CREATE TABLE Enrollments (
    StudentID INT,
    CourseID INT,
    Semester VARCHAR(20),
    Grade VARCHAR(2),
    PRIMARY KEY (StudentID, CourseID, Semester),
    FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
    FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);

Useful Queries:

1. Students with their courses and instructors:

SELECT 
    s.StudentName,
    c.CourseName,
    i.InstructorName,
    e.Grade,
    e.Semester
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID
INNER JOIN Courses c ON e.CourseID = c.CourseID
INNER JOIN CourseInstructors ci ON c.CourseID = ci.CourseID AND e.Semester = ci.Semester
INNER JOIN Instructors i ON ci.InstructorID = i.InstructorID;

2. Find students not enrolled in any courses:

SELECT s.StudentName, s.Email
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
WHERE e.StudentID IS NULL;

3. Courses with no students enrolled:

SELECT c.CourseName, c.Credits
FROM Courses c
LEFT JOIN Enrollments e ON c.CourseID = e.CourseID
WHERE e.CourseID IS NULL;

Quick Reference

Join Types Summary

Join Type Returns Use Case
INNER JOIN Only matching rows from both tables Find related records
LEFT JOIN All from left + matching from right Find all including those without matches
RIGHT JOIN All from right + matching from left Same as LEFT (less common)
FULL OUTER JOIN All rows from both tables Complete picture, data quality
CROSS JOIN Cartesian product (all combinations) Generate combinations
SELF JOIN Table joined with itself Hierarchies, comparisons

Key Types Summary

Key Type Description Rules
Primary Key Unique identifier for each row Must be unique, NOT NULL, one per table
Foreign Key References Primary Key in another table Creates relationships, can be NULL
Composite Key Multiple columns together form key Combination must be unique
Candidate Key Could be a Primary Key Any column(s) that uniquely identify rows
Alternate Key Candidate Key not chosen as PK Enforce with UNIQUE constraint
Surrogate Key Artificial key (auto-increment) No business meaning, best for performance
Natural Key Real-world identifier Has business meaning (SSN, email)
Unique Key Must be unique (not PK) Can have multiple per table, can be NULL

Relationship Types Summary

Relationship Description Implementation
One-to-One One record ↔ One record FK in either table with UNIQUE constraint
One-to-Many One record → Many records FK in the “many” table
Many-to-Many Many ↔ Many Junction table with two FKs
Self-Referencing Table references itself FK references same table’s PK

Best Practices

Database Design Best Practices

1. Primary Keys:

DO:

DON’T:

2. Foreign Keys:

DO:

FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
    ON DELETE CASCADE          -- Delete orders when customer deleted
    ON UPDATE CASCADE          -- Update orders when customer ID changes

DON’T:

3. Relationships:

DO:

DON’T:

4. Joins:

DO:

-- Good: Clear aliases
SELECT c.CustomerName, o.OrderID
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID;

-- Bad: No aliases, hard to read
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

DON’T:

5. Naming Conventions:

DO:

DON’T:


Performance Tips

1. Indexing:

-- Create indexes on foreign keys
CREATE INDEX idx_orders_customer ON Orders(CustomerID);

-- Create indexes on frequently joined columns
CREATE INDEX idx_products_category ON Products(CategoryID);

-- Composite index for multiple columns
CREATE INDEX idx_orders_customer_date ON Orders(CustomerID, OrderDate);

2. Query Optimization:

-- Slow: Function on indexed column
SELECT * FROM Orders WHERE YEAR(OrderDate) = 2024;

-- Fast: Direct comparison
SELECT * FROM Orders WHERE OrderDate >= '2024-01-01' AND OrderDate < '2025-01-01';

3. Avoid N+1 Queries:

-- Bad: Separate query for each customer's orders
SELECT * FROM Customers;
-- Then for each customer:
SELECT * FROM Orders WHERE CustomerID = ?;

-- Good: Single JOIN query
SELECT c.*, o.*
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID;

Common Mistakes to Avoid

Mistake 1: No Foreign Key Constraints

-- ❌ Bad: No constraint
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT  -- Can be any value!
);

-- ✅ Good: With constraint
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT,
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

Mistake 2: Wrong JOIN Type

-- ❌ Bad: Using INNER JOIN when you need all customers
SELECT c.CustomerName, COUNT(o.OrderID) AS OrderCount
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID  -- Excludes customers with no orders
GROUP BY c.CustomerName;

-- ✅ Good: Using LEFT JOIN
SELECT c.CustomerName, COUNT(o.OrderID) AS OrderCount
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID  -- Includes all customers
GROUP BY c.CustomerName;

Mistake 3: Missing Junction Table

-- ❌ Bad: Trying to store multiple authors in one column
CREATE TABLE Books (
    BookID INT PRIMARY KEY,
    Title VARCHAR(200),
    Authors VARCHAR(500)  -- "King, Straub" - NO!
);

-- ✅ Good: Junction table
CREATE TABLE Books (
    BookID INT PRIMARY KEY,
    Title VARCHAR(200)
);

CREATE TABLE Authors (
    AuthorID INT PRIMARY KEY,
    AuthorName VARCHAR(100)
);

CREATE TABLE BookAuthors (
    BookID INT,
    AuthorID INT,
    PRIMARY KEY (BookID, AuthorID),
    FOREIGN KEY (BookID) REFERENCES Books(BookID),
    FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID)
);

Conclusion

Remember the Core Concepts:

  1. Keys identify and relate records
  2. Relationships connect tables logically
  3. Joins combine data from multiple tables

The Foundation:

Start Simple:

Practice Makes Perfect:

Happy database designing! 🗄️