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.
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:
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
| order_id | order_date | first_name | last_name | total |
|---|---|---|---|---|
| 1 | 2024-04-24 | Amanda | Mitchell | 2638.11 |
| 2 | 2024-03-15 | Timothy | Campbell | 2947 |
| 3 | 2023-02-15 | Sarah | Hernandez | 683.24 |
| 4 | 2022-01-10 | Linda | Allen | 3920.44 |
| 5 | 2023-06-16 | Robert | Lewis | 1120.99 |
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.
| Term | Meaning | In the Playground |
|---|---|---|
| Primary key | The column that identifies each row of a table | users.id, orders.id, products.id |
| Foreign key | A column that holds another table's primary key | orders.user_id → users.id |
| Join condition | The rule in ON, almost always foreign key = primary key | ON u.id = o.user_id |
SQL JOIN Syntax
SELECT columns
FROM table_a
[INNER | LEFT | RIGHT | FULL OUTER] JOIN table_b
ON table_a.key = table_b.key;
| Part | What it does |
|---|---|
FROM table_a | The first table, also called the left table. |
JOIN table_b | The second (right) table. With no keyword in front, JOIN means INNER JOIN. |
ON … | The condition that pairs rows of the two tables. |
| Table aliases | Short 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:
| Table | Joins to | On |
|---|---|---|
orders | users | orders.user_id = users.id |
order_items | orders, products | order_items.order_id = orders.id, order_items.product_id = products.id |
payments | orders | payments.order_id = orders.id |
reviews | products, users | reviews.product_id = products.id, reviews.user_id = users.id |
employees | departments, employees | employees.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 type | Returns | Use it for | Lesson |
|---|---|---|---|
| INNER JOIN | Only rows that match in both tables | Orders with their customer | INNER JOIN |
| LEFT JOIN | All rows of the left table, plus matches | All customers, with orders if any | LEFT JOIN |
| RIGHT JOIN | All rows of the right table, plus matches | The same, read from the other side | RIGHT JOIN |
| FULL OUTER JOIN | All rows of both tables | Comparing two lists; finding mismatches | FULL OUTER JOIN |
| CROSS JOIN | Every row paired with every row | Generating combinations | CROSS JOIN |
| SELF JOIN | A table joined to itself | Employees and their managers | SELF 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:
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
| join_type | customers |
|---|---|
| INNER JOIN | 19 |
| LEFT JOIN | 51 |
| RIGHT JOIN | 56 |
| FULL OUTER JOIN | 88 |
| Join | Rows | Who is in the result |
|---|---|---|
| INNER JOIN | 19 | Customers who ordered in both years |
| LEFT JOIN | 51 | Everyone who ordered in 2023 |
| RIGHT JOIN | 56 | Everyone who ordered in 2024 |
| FULL OUTER JOIN | 88 | Everyone 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.
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
| id | first_name | order_id | status |
|---|---|---|---|
| 1 | Jennifer | 109 | delivered |
| 2 | Donna | 13 | delivered |
| 4 | Michael | 11 | processing |
| 4 | Michael | 87 | delivered |
| 4 | Michael | 181 | shipped |
| 5 | Robert | 127 | delivered |
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:
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
| id | first_name | order_id | status |
|---|---|---|---|
| 1 | Jennifer | 109 | delivered |
| 2 | Donna | 13 | delivered |
| 3 | Kenneth | NULL | NULL |
| 4 | Michael | 11 | processing |
| 4 | Michael | 87 | delivered |
| 4 | Michael | 181 | shipped |
| 5 | Robert | 127 | delivered |
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:
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
| customers_without_orders |
|---|
| 36 |
The LEFT JOIN lesson covers this pattern, the ON-versus-WHERE trap and counting with zeros.
RIGHT JOIN
RIGHT JOIN keeps every row of the right table instead. It is a LEFT JOIN with the tables swapped: orders RIGHT JOIN users returns the same rows as users LEFT JOIN orders.
SELECT 'orders RIGHT JOIN users' AS query, COUNT(*) AS rows_returned
FROM orders o
RIGHT JOIN users u ON o.user_id = u.id
UNION ALL
SELECT 'users LEFT JOIN orders', COUNT(*)
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;
▶ Run this query in the SQL Playground
| query | rows_returned |
|---|---|
| orders RIGHT JOIN users | 236 |
| users LEFT JOIN orders | 236 |
Most people write LEFT JOIN only, so the complete table always comes first. See the RIGHT JOIN lesson for when it is still useful.
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:
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
| ordered_2023 | ordered_2024 |
|---|---|
| 2 | NULL |
| NULL | 4 |
| NULL | 5 |
| 6 | NULL |
| 7 | NULL |
| 8 | 8 |
| 10 | 10 |
| NULL | 11 |
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.
UNION to get the same result. PostgreSQL, SQL Server, Oracle and SQLite 3.39+ support it directly.-- 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:
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:
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
| size | colour |
|---|---|
| L | Blue |
| L | Red |
| M | Blue |
| M | Red |
| S | Blue |
| S | Red |
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:
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
| employee | title | manager |
|---|---|---|
| Steven | Paralegal | NULL |
| Michael | Senior Counsel | NULL |
| Michelle | COO | NULL |
| Laura | Sales Manager | William |
| Betty | COO | William |
| Joshua | Content Lead | Michelle |
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 table | INNER JOIN |
| Every row of one table, with extra details where they exist | LEFT JOIN from that table |
| Rows of one table that have no partner | LEFT JOIN … WHERE other.id IS NULL |
| Every row of both tables, matched where possible | FULL OUTER JOIN |
| Every possible combination | CROSS JOIN |
| To compare rows within one table | SELF JOIN |
| To stack rows from two queries, not match them | UNION, 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
| Combines | Result | |
|---|---|---|
| JOIN | Columns from related tables, side by side | Wider rows |
| UNION | Rows from queries with the same columns, stacked | More rows |
| Subquery | One query used inside another | A 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?":
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
| customer | order_date | product | quantity | subtotal |
|---|---|---|---|---|
| Amanda | 2024-04-24 | Giant Stuffed Teddy Bear | 5 | 961.05 |
| Amanda | 2024-04-24 | Marvel Action Figures 5pk | 3 | 321.3 |
| Amanda | 2024-04-24 | Waterproof Hiking Boots | 1 | 5.39 |
Joined rows behave like any other rows, so WHERE, GROUP BY and aggregates work on top. Revenue by product category for delivered orders:
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
| category | units | revenue |
|---|---|---|
| Office Supplies | 92 | 22858.48 |
| Food & Beverage | 69 | 19930.66 |
| Books | 84 | 19514.61 |
| Automotive | 68 | 18032.88 |
| Health & Beauty | 60 | 13963.53 |
Common JOIN Mistakes
Most of these raise no error. The query runs and the answer is wrong.
1. Forgetting the join condition
SELECT o.id, u.first_name
FROM orders o, users u;
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
SELECT u.id, COUNT(o.id)
FROM users u
JOIN orders o
ON o.user_id = u.id
GROUP BY u.id;
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
SELECT u.id, o.id
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
WHERE o.status = 'delivered';
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
SELECT SUM(o.total)
FROM orders o
JOIN payments p
ON p.order_id = o.id;
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
SELECT id, total
FROM orders o
JOIN users u
ON u.id = o.user_id;
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
| Tip | Why it helps |
|---|---|
| Index the join columns | Primary keys are indexed already; foreign keys usually need an index so matches are looked up, not scanned for. |
| Filter early | A WHERE condition that removes rows leaves fewer to match. |
| Select only the columns you need | SELECT * over several tables returns every column of all of them. |
| Join columns of the same type | Comparing a number with text forces a conversion per row and can disable an index. |
| Check the plan | EXPLAIN shows the join order and whether indexes are used. |
SQL JOIN Cheat Sheet
| JOIN | Syntax | Matching rows | Unmatched left rows | Unmatched right rows |
|---|---|---|---|---|
| INNER | a JOIN b ON … | Kept | Dropped | Dropped |
| LEFT | a LEFT JOIN b ON … | Kept | Kept (NULLs) | Dropped |
| RIGHT | a RIGHT JOIN b ON … | Kept | Dropped | Kept (NULLs) |
| FULL OUTER | a FULL OUTER JOIN b ON … | Kept | Kept (NULLs) | Kept (NULLs) |
| CROSS | a CROSS JOIN b | Every pair, no condition | — | — |
| SELF | t a JOIN t b ON … | Any of the above, on one table | — | — |
| Task | Pattern |
|---|---|
| Rows with no match | a LEFT JOIN b ON … WHERE b.id IS NULL |
| Count matches, zeros included | COUNT(b.id) after a LEFT JOIN, grouped by a.id |
| Three tables | a JOIN b ON … JOIN c ON … |
| Limit which rows may match | Add the condition to ON |
| FULL OUTER JOIN in MySQL | LEFT 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.
-
1Easy Show each employee's name and title with their department name, for the first ten employees by id. Table:
employees, departmentsShow solution
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. -
2Easy List customers 20 to 30 with the id of each of their orders. Customers with no orders must still appear. Table:
users, ordersShow solution
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.
-
3Medium How many products have no reviews? Table:
products, reviewsShow solution
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 NULLkeeps only those with no matching review. -
4Medium Show the ten customers who have spent the most: name, number of orders and total spent. Table:
users, ordersShow solution
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.
-
5Medium For each manager, show their name and how many people report to them, most reports first. Table:
employeesShow solution
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:
eis the report andmthe manager. 10 employees manage at least one person. -
6Medium For every department, count the employees who earn more than 150,000. Departments with none must show 0. Table:
departments, employeesShow solution
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.
-
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, productsShow solution
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.
-
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:
ordersShow solution
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
What does a SQL JOIN do?
A join matches rows of different tables on a shared column and returns them side by side.
Which join returns only rows that match in both tables?
INNER JOIN keeps matching pairs only. It is also what plain JOIN means.
You need every customer, including those with no orders. Which join, starting from customers?
LEFT JOIN keeps every row of the first table and fills the order columns with NULL where there is no match.
Which join keeps unmatched rows from both tables?
FULL OUTER JOIN returns everything from both sides, with NULLs where a row has no partner.
A table has 4 rows and another has 5. How many rows does a CROSS JOIN return?
Every row is paired with every row: 4 × 5 = 20.
What is a SELF JOIN?
The same table appears twice under different aliases, for example employees and their managers.
What does the ON clause do?
ON holds the join condition, normally a foreign key compared with the primary key it references.
A RIGHT JOIN B returns the same rows as:
RIGHT JOIN keeps all of B, which is exactly what B LEFT JOIN A does.
How do you find customers who have no orders?
LEFT JOIN keeps all customers; IS NULL on the order key keeps the ones with no match. "= NULL" is never true.
What is the difference between JOIN and UNION?
A join widens rows by matching tables. UNION appends the rows of one query to another with the same columns.
Which database has no native FULL OUTER JOIN?
In MySQL it is emulated with a LEFT JOIN and a RIGHT JOIN combined by UNION.
Why can a total be too high after joining orders to payments?
Each order appears once per payment, so its value is added more than once. Aggregate before joining a one-to-many table.
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.