SQL CROSS JOIN
A CROSS JOIN pairs every row of one table with every row of another, with no join condition. 3 rows × 4 rows = 12 rows. That sounds useless until you need every combination: all sizes in all colours, every product in every month, every employee on every training day. It is also what you get by accident when you forget a join condition.
- Build a Cartesian product with CROSS JOIN
- Generate combinations and report grids
- Fill gaps so missing combinations show 0
- Recognise an accidental cross join
CROSS JOIN syntax
SELECT columnsFROM table1CROSS JOIN table2;Every combination
CREATE TABLE sizes (size VARCHAR(5));CREATE TABLE colours (colour VARCHAR(10));INSERT INTO sizes VALUES ('S'), ('M'), ('L');INSERT INTO colours VALUES ('Black'), ('White');SELECT s.size, c.colour, CONCAT(c.colour, '-', s.size) AS skuFROM sizes sCROSS JOIN colours cORDER BY c.colour, s.size;| size | colour | sku |
|---|---|---|
| L | Black | Black-L |
| M | Black | Black-M |
| S | Black | Black-S |
| L | White | White-L |
| M | White | White-M |
| S | White | White-S |
sizeshas 3 rows andcolourshas 2.CROSS JOINpairs each size with each colour: 3 × 2 = 6 rows.CONCATbuilds a product code for each combination.
Fill gaps in a report
Units sold per category per month. A plain GROUP BY simply leaves out combinations with no sales. Build a grid of every category × every month with a cross join, aggregate the real sales separately, then LEFT JOIN the sales onto the grid:
SELECT g.category, g.month, COALESCE(s.units, 0) AS unitsFROM ( SELECT c.category, m.month FROM (SELECT DISTINCT category FROM products) c CROSS JOIN (SELECT DISTINCT DATE_FORMAT(order_date, '%Y-%m') AS month FROM orders) m) gLEFT JOIN ( SELECT p.category, DATE_FORMAT(o.order_date, '%Y-%m') AS month, SUM(oi.quantity) AS units FROM orders o JOIN order_items oi ON oi.order_id = o.order_id JOIN products p ON p.product_id = oi.product_id GROUP BY p.category, month) s ON s.category = g.category AND s.month = g.monthORDER BY g.category, g.month;| category | month | units |
|---|---|---|
| Electronics | 2025-01 | 1 |
| Electronics | 2025-02 | 2 |
| Electronics | 2025-03 | 2 |
| Electronics | 2025-04 | 1 |
| Electronics | 2025-05 | 1 |
| Electronics | 2025-06 | 0 |
| Furniture | 2025-01 | 1 |
| Furniture | 2025-02 | 1 |
| Furniture | 2025-03 | 1 |
| Furniture | 2025-04 | 4 |
| Furniture | 2025-05 | 0 |
| Furniture | 2025-06 | 0 |
| Stationery | 2025-01 | 5 |
| Stationery | 2025-02 | 0 |
| Stationery | 2025-03 | 14 |
| Stationery | 2025-04 | 0 |
| Stationery | 2025-05 | 0 |
| Stationery | 2025-06 | 5 |
- The grid
gis every category crossed with every month that has orders: 3 × 6 = 18 rows. - The subquery
sis the real sales, one row per category and month that sold something. - The
LEFT JOINkeeps the whole grid; months with no sales for a category getNULL, whichCOALESCEturns into 0.
The accidental cross join
Listing two tables with a comma and forgetting the WHERE produces a cross join:
SELECT COUNT(*) AS rows_returnedFROM employees, departments;| rows_returned |
|---|
| 60 |
FROM employees, departmentswith no condition pairs all 12 employees with all 5 departments.- 12 × 5 = 60 rows, almost all of them meaningless.
- On two tables of 100,000 rows this is 10 billion rows and can bring a server down. Always write explicit
JOIN ... ON.
CROSS JOIN with a single row
Crossing with a one-row result attaches a value to every row, for example the company average:
SELECT e.first_name, e.salary, a.avg_salary, e.salary - a.avg_salary AS diff_from_avgFROM employees eCROSS JOIN (SELECT ROUND(AVG(salary)) AS avg_salary FROM employees) aWHERE e.dept_id = 1;| first_name | salary | avg_salary | diff_from_avg |
|---|---|---|---|
| Asha | 125000 | 88333 | 36667 |
| Rahul | 98000 | 88333 | 9667 |
| Priya | 72000 | 88333 | -16333 |
| Karan | 72000 | 88333 | -16333 |
| Amit | 25000 | 88333 | -63333 |
Quick quiz
1. Table A has 5 rows and B has 4. How many rows does A CROSS JOIN B return?
Every row of A is paired with every row of B.
2. What does SELECT * FROM a, b without WHERE produce?
A comma join with no condition is a Cartesian product.
3. Why cross join categories with months in a report?
The grid guarantees every combination is present.
CROSS JOIN FAQ
What is a Cartesian product in SQL?
The set of all combinations of rows from two tables. A CROSS JOIN returns it.
What is the difference between CROSS JOIN and INNER JOIN?
CROSS JOIN has no join condition and returns every combination. INNER JOIN returns only the combinations that satisfy the ON condition.
Is CROSS JOIN slow?
The result grows as rows × rows, so it is fine for small lookup lists but dangerous on large tables.