SQLab Hub
Lesson 12 · SQL JOINs
Lesson 12 · Querying

SQL JOINs: Types, Examples & Cheat Sheet

A SQL JOIN combines rows from two or more tables by matching a column they share. The join type decides what happens to rows that have no match: INNER JOIN drops them, LEFT, RIGHT and FULL OUTER JOIN keep them, and CROSS JOIN pairs everything with everything.

⋈ All 6 join types ▶ Runnable Playground examples 📋 JOIN cheat sheet 🧪 8 practice exercises 📝 12-question quiz 🎤 15 interview questions

What Is a JOIN in SQL?

Databases store each kind of thing in its own table. In the Playground database an order only records who placed it as a number, user_id; the customer's name is in the users table. A JOIN brings the two together by matching orders.user_id with users.id:

SQLite · runs in the Playground
SELECT o.id AS order_id, o.order_date,
       u.first_name, u.last_name, o.total
FROM orders o
JOIN users u
  ON u.id = o.user_id
ORDER BY o.id
LIMIT 5;

▶ Run this query in the SQL Playground

Orders with the name of the customer who placed them
order_idorder_datefirst_namelast_nametotal
12024-04-24AmandaMitchell2638.11
22024-03-15TimothyCampbell2947
32023-02-15SarahHernandez683.24
42022-01-10LindaAllen3920.44
52023-06-16RobertLewis1120.99
✓ 5 rows · real output from the Playground database

Every row of the result is built from two tables. ON states which rows belong together, and o and u are aliases that say which table each column comes from.

Why data is split across tables

If every order row repeated the customer's name, email and city, a change of email would have to be made in every one of that customer's orders. Keeping customers in one table and orders in another stores each fact once. This design is called normalisation, and JOIN is how the pieces are put back together for a report.

TermMeaningIn the Playground
Primary keyThe column that identifies each row of a tableusers.id, orders.id, products.id
Foreign keyA column that holds another table's primary keyorders.user_id → users.id
Join conditionThe rule in ON, almost always foreign key = primary keyON u.id = o.user_id

SQL JOIN Syntax

SQL · syntax
SELECT columns
FROM table_a
[INNER | LEFT | RIGHT | FULL OUTER] JOIN table_b
  ON table_a.key = table_b.key;
PartWhat it does
FROM table_aThe first table, also called the left table.
JOIN table_bThe second (right) table. With no keyword in front, JOIN means INNER JOIN.
ON …The condition that pairs rows of the two tables.
Table aliasesShort names (orders o) used to prefix columns and avoid "ambiguous column" errors.

How the Playground tables connect

Every example on this page runs on these tables. The arrows are the foreign keys you join on:

TableJoins toOn
ordersusersorders.user_id = users.id
order_itemsorders, productsorder_items.order_id = orders.id, order_items.product_id = products.id
paymentsorderspayments.order_id = orders.id
reviewsproducts, usersreviews.product_id = products.id, reviews.user_id = users.id
employeesdepartments, employeesemployees.department_id = departments.id, employees.manager_id = employees.id

Types of SQL JOINs

Six joins, one question: what should happen to rows that have no match?

JOIN typeReturnsUse it forLesson
INNER JOINOnly rows that match in both tablesOrders with their customerINNER JOIN
LEFT JOINAll rows of the left table, plus matchesAll customers, with orders if anyLEFT JOIN
RIGHT JOINAll rows of the right table, plus matchesThe same, read from the other sideRIGHT JOIN
FULL OUTER JOINAll rows of both tablesComparing two lists; finding mismatchesFULL OUTER JOIN
CROSS JOINEvery row paired with every rowGenerating combinationsCROSS JOIN
SELF JOINA table joined to itselfEmployees and their managersSELF JOIN

The four main joins on the same data

To see INNER, LEFT, RIGHT and FULL give four different answers, both tables need rows the other lacks. Take the customers who ordered in 2023 as the left table and those who ordered in 2024 as the right table, then count the rows each join returns:

SQLite · runs in the Playground
WITH y2023 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
), y2024 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
)
SELECT 'INNER JOIN' AS join_type, COUNT(*) AS customers
FROM y2023 a INNER JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'LEFT JOIN', COUNT(*)
FROM y2023 a LEFT JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'RIGHT JOIN', COUNT(*)
FROM y2023 a RIGHT JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'FULL OUTER JOIN', COUNT(*)
FROM y2023 a FULL OUTER JOIN y2024 b ON a.user_id = b.user_id;

▶ Run this query in the SQL Playground

Rows returned by each join between 2023 buyers and 2024 buyers
join_typecustomers
INNER JOIN19
LEFT JOIN51
RIGHT JOIN56
FULL OUTER JOIN88
✓ 4 rows · real output from the Playground database
JoinRowsWho is in the result
INNER JOIN19Customers who ordered in both years
LEFT JOIN51Everyone who ordered in 2023
RIGHT JOIN56Everyone who ordered in 2024
FULL OUTER JOIN88Everyone who ordered in either year

The numbers fit together: 51 + 56 − 19 = 88. The two WITH blocks are CTEs, named subqueries that stand in for two tables.

INNER JOIN

INNER JOIN returns a row for every pair that satisfies ON, and nothing else. It is the default and the most used join.

SQLite · runs in the Playground
SELECT u.id, u.first_name, o.id AS order_id, o.status
FROM users u
INNER JOIN orders o
  ON o.user_id = u.id
WHERE u.id <= 5
ORDER BY u.id, o.id;

▶ Run this query in the SQL Playground

Orders of the first five customers with INNER JOIN
idfirst_nameorder_idstatus
1Jennifer109delivered
2Donna13delivered
4Michael11processing
4Michael87delivered
4Michael181shipped
5Robert127delivered
✓ 6 rows · real output from the Playground database

Customer 3 has no orders and is missing. Customer 4 has three and appears three times. More in the INNER JOIN lesson, including joins across three and four tables.

LEFT JOIN

LEFT JOIN keeps every row of the left table. Where there is no match, the right table's columns are NULL. The same five customers again:

SQLite · runs in the Playground
SELECT u.id, u.first_name, o.id AS order_id, o.status
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
WHERE u.id <= 5
ORDER BY u.id, o.id;

▶ Run this query in the SQL Playground

The first five customers with LEFT JOIN, including the one without orders
idfirst_nameorder_idstatus
1Jennifer109delivered
2Donna13delivered
3KennethNULLNULL
4Michael11processing
4Michael87delivered
4Michael181shipped
5Robert127delivered
✓ 7 rows · real output from the Playground database

Customer 3 is back, with NULL order columns. Filtering on that NULL finds rows with no match at all, such as customers who never ordered:

SQLite · runs in the Playground
SELECT COUNT(*) AS customers_without_orders
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
WHERE o.id IS NULL;

▶ Run this query in the SQL Playground

Number of customers with no orders
customers_without_orders
36
✓ 1 row · real output from the Playground database

The LEFT JOIN lesson covers this pattern, the ON-versus-WHERE trap and counting with zeros.

FULL OUTER JOIN

FULL OUTER JOIN keeps unmatched rows from both tables. NULLs can appear on either side, which makes it the join for comparing two lists:

SQLite · runs in the Playground
WITH y2023 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
), y2024 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
)
SELECT a.user_id AS ordered_2023,
       b.user_id AS ordered_2024
FROM y2023 a
FULL OUTER JOIN y2024 b
  ON a.user_id = b.user_id
ORDER BY COALESCE(a.user_id, b.user_id)
LIMIT 8;

▶ Run this query in the SQL Playground

Customers who ordered in 2023, 2024 or both
ordered_2023ordered_2024
2NULL
NULL4
NULL5
6NULL
7NULL
88
1010
NULL11
✓ 8 rows · real output from the Playground database

A value in both columns means the customer ordered in both years; a NULL on the left means new in 2024; a NULL on the right means they did not come back.

MySQL has no FULL OUTER JOIN. Combine a LEFT JOIN and a RIGHT JOIN with UNION to get the same result. PostgreSQL, SQL Server, Oracle and SQLite 3.39+ support it directly.
MySQL
-- FULL OUTER JOIN in MySQL
SELECT a.id, b.id FROM a LEFT JOIN b ON b.a_id = a.id
UNION
SELECT a.id, b.id FROM a RIGHT JOIN b ON b.a_id = a.id;

Details in the FULL OUTER JOIN lesson.

CROSS JOIN

CROSS JOIN has no ON clause. It pairs every row of one table with every row of the other, so 3 sizes and 2 colours give 3 × 2 = 6 combinations:

SQL
SELECT s.size, c.colour
FROM sizes s
CROSS JOIN colours c;

The Playground blocks the CROSS JOIN keyword to protect the shared database, so this runnable version writes the same join the older way, with a comma between two small lists:

SQLite · runs in the Playground
SELECT s.size, c.colour
FROM (SELECT 'S' AS size UNION ALL SELECT 'M' UNION ALL SELECT 'L') AS s,
     (SELECT 'Red' AS colour UNION ALL SELECT 'Blue') AS c
ORDER BY s.size, c.colour;

▶ Run this query in the SQL Playground

Every combination of three sizes and two colours
sizecolour
LBlue
LRed
MBlue
MRed
SBlue
SRed
✓ 6 rows · real output from the Playground database
Row counts multiply. Cross joining two 1,000-row tables returns 1,000,000 rows. An accidental cross join, usually a forgotten join condition, is the most common cause of a query that never finishes.

Uses and safeguards are in the CROSS JOIN lesson.

SELF JOIN

A self join is not a keyword; it is any join of a table to itself. In employees, manager_id holds the id of another employee, so the table is joined to a second copy of itself under a different alias:

SQLite · runs in the Playground
SELECT e.first_name AS employee, e.title,
       m.first_name AS manager
FROM employees e
LEFT JOIN employees m
  ON m.id = e.manager_id
WHERE e.id BETWEEN 8 AND 13
ORDER BY e.id;

▶ Run this query in the SQL Playground

Employees with their manager
employeetitlemanager
StevenParalegalNULL
MichaelSenior CounselNULL
MichelleCOONULL
LauraSales ManagerWilliam
BettyCOOWilliam
JoshuaContent LeadMichelle
✓ 6 rows · real output from the Playground database

e is the employee and m the manager. A LEFT JOIN is used so that people with no manager are kept. More in the SELF JOIN lesson.

Which JOIN Should You Use?

You want…Use
Only rows that have a partner in the other tableINNER JOIN
Every row of one table, with extra details where they existLEFT JOIN from that table
Rows of one table that have no partnerLEFT JOIN … WHERE other.id IS NULL
Every row of both tables, matched where possibleFULL OUTER JOIN
Every possible combinationCROSS JOIN
To compare rows within one tableSELF JOIN
To stack rows from two queries, not match themUNION, not a join

A quick test: ask what should happen to a customer with no orders. If they should disappear, it is an INNER JOIN; if they should stay, it is a LEFT JOIN.

JOIN vs UNION vs subquery

CombinesResult
JOINColumns from related tables, side by sideWider rows
UNIONRows from queries with the same columns, stackedMore rows
SubqueryOne query used inside anotherA value, list or table for the outer query

Joining Three or More Tables

Add one JOIN … ON per table. Each new table needs a condition linking it to a table already in the query. Four tables answer "what did the customer buy in order 1?":

SQLite · runs in the Playground
SELECT u.first_name AS customer, o.order_date,
       p.name AS product, oi.quantity, oi.subtotal
FROM order_items oi
JOIN orders o   ON o.id = oi.order_id
JOIN users u    ON u.id = o.user_id
JOIN products p ON p.id = oi.product_id
WHERE o.id = 1
ORDER BY oi.id;

▶ Run this query in the SQL Playground

Items of order 1 with customer and product names
customerorder_dateproductquantitysubtotal
Amanda2024-04-24Giant Stuffed Teddy Bear5961.05
Amanda2024-04-24Marvel Action Figures 5pk3321.3
Amanda2024-04-24Waterproof Hiking Boots15.39
✓ 3 rows · real output from the Playground database

Joined rows behave like any other rows, so WHERE, GROUP BY and aggregates work on top. Revenue by product category for delivered orders:

SQLite · runs in the Playground
SELECT p.category,
       SUM(oi.quantity) AS units,
       ROUND(SUM(oi.subtotal), 2) AS revenue
FROM order_items oi
JOIN orders o   ON o.id = oi.order_id
JOIN products p ON p.id = oi.product_id
WHERE o.status = 'delivered'
GROUP BY p.category
ORDER BY revenue DESC;

▶ Run this query in the SQL Playground

Units and revenue per product category for delivered orders
categoryunitsrevenue
Office Supplies9222858.48
Food & Beverage6919930.66
Books8419514.61
Automotive6818032.88
Health & Beauty6013963.53
✓ 10 rows · first 5 shown · real output from the Playground database

Common JOIN Mistakes

Most of these raise no error. The query runs and the answer is wrong.

1. Forgetting the join condition

❌ Every order paired with every customer
SQL
SELECT o.id, u.first_name
FROM orders o, users u;
✅ Matched on the key
SQL
SELECT o.id, u.first_name
FROM orders o
JOIN users u
  ON u.id = o.user_id;

The first query returns 200 × 150 = 30,000 rows. Writing JOIN … ON keeps the condition next to the join, and PostgreSQL and SQL Server reject a JOIN that has no ON at all.

2. INNER JOIN when rows must be kept

❌ Customers without orders vanish
SQL
SELECT u.id, COUNT(o.id)
FROM users u
JOIN orders o
  ON o.user_id = u.id
GROUP BY u.id;
✅ LEFT JOIN keeps them
SQL
SELECT u.id, COUNT(o.id)
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
GROUP BY u.id;

An inner join cannot produce a zero count, because customers with no orders are not in the result to be counted.

3. Filtering an outer join in WHERE

❌ Behaves like an INNER JOIN
SQL
SELECT u.id, o.id
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
WHERE o.status = 'delivered';
✅ Condition in ON
SQL
SELECT u.id, o.id
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
 AND o.status = 'delivered';

For customers with no orders o.status is NULL, so the WHERE test fails and they are removed. See ON vs WHERE.

4. Summing across a one-to-many join

❌ Order totals counted once per payment
SQL
SELECT SUM(o.total)
FROM orders o
JOIN payments p
  ON p.order_id = o.id;
✅ Sum before joining
SQL
SELECT SUM(total)
FROM orders;

An order with two payments appears twice in the join, so its total is added twice. Aggregate the detail table first, or sum values only from the "many" side.

5. Unqualified column names

❌ Ambiguous: both tables have id
SQL
SELECT id, total
FROM orders o
JOIN users u
  ON u.id = o.user_id;
✅ Alias every column
SQL
SELECT o.id, o.total
FROM orders o
JOIN users u
  ON u.id = o.user_id;

When two joined tables share a column name the database cannot tell which one you mean and stops with an error.

JOIN Performance

TipWhy it helps
Index the join columnsPrimary keys are indexed already; foreign keys usually need an index so matches are looked up, not scanned for.
Filter earlyA WHERE condition that removes rows leaves fewer to match.
Select only the columns you needSELECT * over several tables returns every column of all of them.
Join columns of the same typeComparing a number with text forces a conversion per row and can disable an index.
Check the planEXPLAIN shows the join order and whether indexes are used.

SQL JOIN Cheat Sheet

JOINSyntaxMatching rowsUnmatched left rowsUnmatched right rows
INNERa JOIN b ON …KeptDroppedDropped
LEFTa LEFT JOIN b ON …KeptKept (NULLs)Dropped
RIGHTa RIGHT JOIN b ON …KeptDroppedKept (NULLs)
FULL OUTERa FULL OUTER JOIN b ON …KeptKept (NULLs)Kept (NULLs)
CROSSa CROSS JOIN bEvery pair, no condition——
SELFt a JOIN t b ON …Any of the above, on one table——
TaskPattern
Rows with no matcha LEFT JOIN b ON … WHERE b.id IS NULL
Count matches, zeros includedCOUNT(b.id) after a LEFT JOIN, grouped by a.id
Three tablesa JOIN b ON … JOIN c ON …
Limit which rows may matchAdd the condition to ON
FULL OUTER JOIN in MySQLLEFT JOIN UNION RIGHT JOIN

Practice SQL JOINs Queries

8 exercises on the real Playground tables.

Write each query yourself first. Every solution has been run against the Playground database.

  1. 1Easy Show each employee's name and title with their department name, for the first ten employees by id. Table: employees, departments

    Show solution
    SQLite · runs in the Playground
    SELECT e.first_name, e.last_name, e.title,
           d.name AS department
    FROM employees e
    JOIN departments d
      ON d.id = e.department_id
    ORDER BY e.id
    LIMIT 10;

    ▶ Run this query in the SQL Playground

    An inner join on the foreign key employees.department_id.

  2. 2Easy List customers 20 to 30 with the id of each of their orders. Customers with no orders must still appear. Table: users, orders

    Show solution
    SQLite · runs in the Playground
    SELECT u.id, u.first_name, o.id AS order_id
    FROM users u
    LEFT JOIN orders o
      ON o.user_id = u.id
    WHERE u.id BETWEEN 20 AND 30
    ORDER BY u.id, o.id;

    ▶ Run this query in the SQL Playground

    "Must still appear" means LEFT JOIN. The result has 15 rows; customers without orders show NULL.

  3. 3Medium How many products have no reviews? Table: products, reviews

    Show solution
    SQLite · runs in the Playground
    SELECT COUNT(*) AS products_without_reviews
    FROM products p
    LEFT JOIN reviews r
      ON r.product_id = p.id
    WHERE r.id IS NULL;

    ▶ Run this query in the SQL Playground

    The LEFT JOIN keeps every product, and r.id IS NULL keeps only those with no matching review.

  4. 4Medium Show the ten customers who have spent the most: name, number of orders and total spent. Table: users, orders

    Show solution
    SQLite · runs in the Playground
    SELECT u.first_name, u.last_name,
           COUNT(*) AS orders,
           ROUND(SUM(o.total), 2) AS total_spent
    FROM users u
    JOIN orders o
      ON o.user_id = u.id
    GROUP BY u.id, u.first_name, u.last_name
    ORDER BY total_spent DESC
    LIMIT 10;

    ▶ Run this query in the SQL Playground

    Only customers with orders can be top spenders, so an INNER JOIN is the right choice here.

  5. 5Medium For each manager, show their name and how many people report to them, most reports first. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT m.first_name, m.last_name,
           COUNT(*) AS direct_reports
    FROM employees e
    JOIN employees m
      ON m.id = e.manager_id
    GROUP BY m.id, m.first_name, m.last_name
    ORDER BY direct_reports DESC, m.id;

    ▶ Run this query in the SQL Playground

    A self join: e is the report and m the manager. 10 employees manage at least one person.

  6. 6Medium For every department, count the employees who earn more than 150,000. Departments with none must show 0. Table: departments, employees

    Show solution
    SQLite · runs in the Playground
    SELECT d.name AS department,
           COUNT(e.id) AS high_earners
    FROM departments d
    LEFT JOIN employees e
      ON e.department_id = d.id
     AND e.salary > 150000
    GROUP BY d.id, d.name
    ORDER BY high_earners DESC, d.name;

    ▶ Run this query in the SQL Playground

    The salary condition is in ON so that it limits the matches without removing departments.

  7. 7Challenging List the five largest order lines by subtotal, with the customer's name, the product name and the order date. Table: order_items, orders, users, products

    Show solution
    SQLite · runs in the Playground
    SELECT u.first_name, u.last_name,
           p.name AS product, o.order_date, oi.subtotal
    FROM order_items oi
    JOIN orders o   ON o.id = oi.order_id
    JOIN users u    ON u.id = o.user_id
    JOIN products p ON p.id = oi.product_id
    ORDER BY oi.subtotal DESC, oi.id
    LIMIT 5;

    ▶ Run this query in the SQL Playground

    Four tables need three join conditions, each linking a new table to one already in the query.

  8. 8Challenging Using a FULL OUTER JOIN between 2023 buyers and 2024 buyers, count the customers who ordered in 2023 only, in 2024 only, and in both. Table: orders

    Show solution
    SQLite · runs in the Playground
    WITH y2023 AS (
      SELECT DISTINCT user_id FROM orders
      WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
    ), y2024 AS (
      SELECT DISTINCT user_id FROM orders
      WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
    )
    SELECT CASE WHEN a.user_id IS NULL THEN '2024 only'
                WHEN b.user_id IS NULL THEN '2023 only'
                ELSE 'both years' END AS segment,
           COUNT(*) AS customers
    FROM y2023 a
    FULL OUTER JOIN y2024 b
      ON a.user_id = b.user_id
    GROUP BY segment
    ORDER BY segment;

    ▶ Run this query in the SQL Playground

    The side that is NULL tells you which list the customer is missing from. CASE WHEN turns that into a label.

📝 Quiz — SQL JOINs

Test your understanding · 0/12 answered

Question 1 of 12

What does a SQL JOIN do?

Question 2 of 12

Which join returns only rows that match in both tables?

Question 3 of 12

You need every customer, including those with no orders. Which join, starting from customers?

Question 4 of 12

Which join keeps unmatched rows from both tables?

Question 5 of 12

A table has 4 rows and another has 5. How many rows does a CROSS JOIN return?

Question 6 of 12

What is a SELF JOIN?

Question 7 of 12

What does the ON clause do?

Question 8 of 12

A RIGHT JOIN B returns the same rows as:

Question 9 of 12

How do you find customers who have no orders?

Question 10 of 12

What is the difference between JOIN and UNION?

Question 11 of 12

Which database has no native FULL OUTER JOIN?

Question 12 of 12

Why can a total be too high after joining orders to payments?

SQL JOINs Interview Questions

Q1. What is a JOIN in SQL?

An operation that combines rows from two or more tables based on a related column, usually a foreign key matched to the primary key it references.

Q2. What are the types of joins?

INNER JOIN (matches only), LEFT and RIGHT OUTER JOIN (all rows of one side plus matches), FULL OUTER JOIN (all rows of both), CROSS JOIN (every combination), and SELF JOIN, which is any of these applied to one table twice.

Q3. What is the difference between INNER JOIN and LEFT JOIN?

INNER JOIN returns only matching rows. LEFT JOIN also returns the left-table rows with no match, with NULL in the right table's columns.

Q4. What is the difference between LEFT JOIN and RIGHT JOIN?

Only which table is kept in full. A RIGHT JOIN B is the same as B LEFT JOIN A.

Q5. What does FULL OUTER JOIN return?

All matching rows plus the unmatched rows of both tables, padded with NULLs. MySQL does not support it and needs a UNION of a LEFT and a RIGHT join.

Q6. What is a CROSS JOIN?

The Cartesian product: every row of one table paired with every row of the other, with no join condition. The row count is the product of the two tables' row counts.

Q7. What is a SELF JOIN and when is it used?

A join of a table to itself under two aliases. It is used for hierarchies such as employee and manager, and for comparing rows within a table.

Q8. What is the difference between ON and WHERE?

ON decides how rows are matched; WHERE filters the result afterwards. For inner joins the result is the same either way. For outer joins a WHERE condition on the optional table removes the NULL rows and turns it into an inner join.

Q9. How do you find rows in one table with no match in another?

LEFT JOIN and keep rows WHERE other.id IS NULL, or use NOT EXISTS. NOT IN is unsafe when the subquery can return NULL.

Q10. What is the difference between JOIN and UNION?

JOIN combines columns of related tables side by side. UNION stacks the rows of two queries that return the same number and type of columns.

Q11. What is the difference between a JOIN and a subquery?

A join returns columns from several tables together. A subquery is a query nested inside another and can often be rewritten as a join. Joins are usually clearer when you need columns from both tables; subqueries when you only need to test or filter.

Q12. How do you join three tables?

FROM a JOIN b ON … JOIN c ON …. Each added table needs its own condition linking it to a table already present, so n tables need at least n − 1 conditions.

Q13. Why does a join return duplicate rows?

A row is returned once per match. In a one-to-many relationship the "one" side repeats. Group the result or aggregate the "many" table first if one row per parent is needed.

Q14. What happens to NULLs in the join column?

NULL equals nothing, so those rows never match. An inner join drops them; an outer join keeps them on the preserved side with NULLs opposite.

Q15. How do you make joins fast?

Index the join columns, especially foreign keys; filter early; join columns of the same data type; select only needed columns; and read the execution plan.

Frequently Asked Questions

What is a JOIN in SQL?

A JOIN combines rows from two or more tables based on a related column, so data stored in separate tables can be returned in one result.

What are the different types of SQL JOINs?

The six usually listed are INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, CROSS JOIN and SELF JOIN. The first four differ in which unmatched rows they keep, CROSS JOIN returns every combination, and SELF JOIN joins a table to itself.

What is the difference between INNER JOIN and LEFT JOIN?

INNER JOIN returns only rows that have a match in both tables. LEFT JOIN returns those rows and also every unmatched row of the left table, with NULL in the right table's columns.

Which JOIN is used most often?

INNER JOIN and LEFT JOIN cover the great majority of queries. INNER JOIN is used when only matching rows matter, LEFT JOIN when the first table must stay complete.

Is JOIN the same as INNER JOIN?

Yes. When no type is written, JOIN means INNER JOIN in every major database.

Can I join more than two tables?

Yes. Add one JOIN … ON clause for each additional table, each with a condition linking it to a table already in the query.

What is the difference between JOIN and UNION?

JOIN matches rows of different tables and places their columns side by side. UNION stacks the rows of two queries that have the same columns.

Does MySQL support FULL OUTER JOIN?

No. In MySQL, combine a LEFT JOIN and a RIGHT JOIN with UNION to get the same result. PostgreSQL, SQL Server, Oracle and recent SQLite versions support FULL OUTER JOIN directly.