SQL JOINs

SQL JOINs combine rows from two or more tables using a related column. Real databases split data into separate tables to avoid repetition: the Orders table stores only a CustomerID, and a JOIN brings back the customer name when you need it.

INNER JOIN

INNER JOIN returns only rows that have a match in both tables. Here every order is shown together with its customer name.

SELECT o.OrderID, c.CustomerName, o.OrderDate
FROM Orders AS o
INNER JOIN Customers AS c ON o.CustomerID = c.CustomerID
ORDER BY o.OrderID;
OrderID | CustomerName   | OrderDate
--------+----------------+-----------
101     | Alpha Traders  | 2026-01-05
102     | Blue Sky Foods | 2026-01-09
103     | Cedar Market   | 2026-02-11
104     | Alpha Traders  | 2026-02-14
105     | Evergreen Co   | 2026-03-02
106     | Harbor Goods   | 2026-03-20
107     | Blue Sky Foods | 2026-04-01
108     | Golden Grains  | 2026-04-18
109     | Indigo Retail  | 2026-05-07
110     | Cedar Market   | 2026-05-22
(10 rows)

Joining Three Tables

Add another JOIN for every extra table. This shows who bought what and the line total.

SELECT o.OrderID, c.CustomerName, p.ProductName,
       o.Quantity * p.Price AS LineTotal
FROM Orders o
JOIN Customers c ON c.CustomerID = o.CustomerID
JOIN Products p ON p.ProductID = o.ProductID
ORDER BY LineTotal DESC
LIMIT 5;
OrderID | CustomerName   | ProductName    | LineTotal
--------+----------------+----------------+----------
106     | Harbor Goods   | Coffee Beans   | 264
108     | Golden Grains  | Wheat Flour    | 240
105     | Evergreen Co   | Dark Chocolate | 170
102     | Blue Sky Foods | Green Tea      | 150
101     | Alpha Traders  | Basmati Rice   | 125
(5 rows)

LEFT JOIN

LEFT JOIN keeps every row of the left table, even without a match. Missing values from the right table become NULL. This finds customers who never ordered.

SELECT c.CustomerName, o.OrderID
FROM Customers c
LEFT JOIN Orders o ON o.CustomerID = c.CustomerID
WHERE o.OrderID IS NULL;
CustomerName   | OrderID
---------------+--------
Delta Supplies | NULL
Fresh Mart     | NULL
Jade Imports   | NULL
(3 rows)

RIGHT JOIN and FULL OUTER JOIN

RIGHT JOIN keeps every row of the right table; FULL OUTER JOIN keeps unmatched rows from both. They are supported in MySQL (RIGHT only), SQL Server, PostgreSQL and SQLite 3.39+. A RIGHT JOIN can always be rewritten as a LEFT JOIN by swapping the tables, which is why many teams use only LEFT JOIN.

Join Types at a Glance

  • INNER JOIN: matching rows only
  • LEFT JOIN: all rows from the left table + matches
  • RIGHT JOIN: all rows from the right table + matches
  • FULL OUTER JOIN: all rows from both tables
  • CROSS JOIN: every combination of rows (covered in Advanced Joins)

Frequently asked questions

What is the difference between INNER JOIN and LEFT JOIN?

INNER JOIN returns only rows with a match in both tables. LEFT JOIN returns all rows from the left table, filling columns from the right table with NULL when there is no match.

Is JOIN the same as INNER JOIN?

Yes. Writing just JOIN means INNER JOIN in every major database.

Why does my JOIN return duplicate rows?

If one row matches several rows in the other table, it appears once per match. That is expected; check that you are joining on the correct key, or aggregate the results.