Introduction to PostgreSQL CROSS JOIN
A CROSS JOIN combines each row from the first table with every row from the second table, producing the Cartesian product of the two tables. Unlike INNER JOIN or LEFT JOIN, CROSS JOIN has no join condition.
Syntax:
SELECT select_list
FROM table1
CROSS JOIN table2;
Equivalent comma syntax:
SELECT select_list
FROM table1, table2;
Equivalent using INNER JOIN ON true:
SELECT select_list
FROM table1
INNER JOIN table2 ON true;
If table1 has n rows and table2 has m rows, the result contains n × m rows.
Basic CROSS JOIN example
Create two small tables and cross-join them:
CREATE TABLE T1 (label CHAR(1) PRIMARY KEY);
CREATE TABLE T2 (score INT PRIMARY KEY);
INSERT INTO T1 VALUES ('A'), ('B');
INSERT INTO T2 VALUES (1), (2), (3);
SELECT * FROM T1 CROSS JOIN T2;
Output (2 × 3 = 6 rows):
label | score
-------+-------
A | 1
B | 1
A | 2
B | 2
A | 3
B | 3
(6 rows)
Practical examples
Scheduling — all employee-shift combinations:
-- All possible employee-shift pairings for staffing analysis
SELECT e.employee_name, s.shift_name
FROM employees e
CROSS JOIN shifts s
ORDER BY e.employee_name, s.shift_name;
Inventory — every product in every warehouse:
-- All product-warehouse combinations to identify stocking gaps
SELECT p.product_name, w.warehouse_name
FROM products p
CROSS JOIN warehouses w
ORDER BY w.warehouse_name, p.product_name;
CROSS JOIN tips
- Calculate the expected row count (n × m) before running to avoid accidentally generating millions of rows.
- Use the explicit
CROSS JOINkeyword rather than the comma syntax — it makes the intent clear to anyone reading the query. - Add a
WHEREclause after the join to filter the Cartesian product down to only the combinations you need. - During development, use
LIMITto inspect the output shape before retrieving the full result set. - Be especially careful when either table is large — a 1,000-row table crossed with another 1,000-row table produces 1,000,000 rows.