CROSS JOIN
IntermediateCROSS JOIN produces the Cartesian product of two tables — every row from the left table paired with every row from the right, yielding m×n rows.
Overview
CROSS JOIN has no join predicate — every row of the first table is combined with every row of the second. A CROSS JOIN of a 1,000-row table and a 500-row table produces 500,000 rows. Accidental Cartesian products (forgetting the ON clause in an implicit join) are a common source of runaway queries that kill production databases. Intentional uses of CROSS JOIN include generating a complete date spine (pairing a dates table with a users table to fill in missing time-series data), generating test data combinations, and building "all-pairs" combination tables. Always add a row-count sanity check before running a CROSS JOIN on large tables.
CROSS JOIN Syntax and Use Cases
CROSS JOIN is explicit and obvious. The implicit equivalent is a comma-separated FROM clause without a WHERE join condition — which is the accidental Cartesian product anti-pattern.
-- Explicit CROSS JOIN: all size-colour combinations for a product catalogue
SELECT
s.size_name,
c.colour_name
FROM sizes s
CROSS JOIN colours c
ORDER BY s.size_name, c.colour_name;
-- If sizes has 5 rows and colours has 8 rows → 40 rows
-- Date spine: generate a row for every (user, date) pair in the last 7 days
-- Then LEFT JOIN to actual orders to show "0 orders" days
WITH date_spine AS (
SELECT generate_series(
CURRENT_DATE - INTERVAL '6 days',
CURRENT_DATE,
'1 day'::INTERVAL
)::DATE AS day
),
active_users AS (
SELECT id, email FROM users WHERE status = 'active'
)
SELECT
au.email,
ds.day,
COALESCE(SUM(o.total_amount), 0) AS daily_revenue
FROM active_users au
CROSS JOIN date_spine ds
LEFT JOIN orders o ON o.user_id = au.id
AND o.created_at::DATE = ds.day
GROUP BY au.email, ds.day
ORDER BY au.email, ds.day;Accidental Cartesian Product Anti-Pattern
Forgetting the ON clause or an implicit join condition causes an accidental Cartesian product. These queries are silent — they return results, just wrong ones, and can bring down a production database.
-- Anti-pattern: implicit join missing WHERE condition = accidental Cartesian product
SELECT o.id, u.email
FROM orders o, users u
WHERE o.user_id = u.id -- MISSING "WHERE o.user_id = u.id"
-- Result: every order × every user = potentially billions of rows!
-- Anti-pattern: CROSS JOIN in multi-table implicit join (easy to miss)
SELECT o.id, u.email, p.name
FROM orders o, users u, products p
WHERE o.user_id = u.id; -- forgets "AND o.product_id = p.id"
-- Safe guard: estimate row count before running
SELECT
(SELECT COUNT(*) FROM sizes) *
(SELECT COUNT(*) FROM colours) AS estimated_cross_join_rows;
-- PostgreSQL: generate_series as a CROSS JOIN-like row generator
SELECT day::DATE
FROM generate_series('2024-01-01', '2024-12-31', '1 day'::INTERVAL) AS t(day);Key Points to Remember
- 1CROSS JOIN = Cartesian product: m × n rows with no join predicate.
- 2Intentional use: date spine generation, all-pairs combination tables, test data.
- 3Accidental Cartesian product: implicit join with a missing WHERE condition.
- 4PostgreSQL generate_series() is a cleaner alternative for date/integer sequences.
- 5Always estimate the output row count (m × n) before running a CROSS JOIN on large tables.
- 6Accidental Cartesian products return "correct-looking" wrong results — they do not error.
Interview Questions
Sign in to ask AriaWhat is a Cartesian product and when would you intentionally use a CROSS JOIN?
How would you generate a complete date range in SQL for a time-series gap-fill query?
What is the most common cause of accidental Cartesian products in production SQL?
Ask Aria about CROSS JOIN
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.