πŸŽ‰ 75% of content is free forever β€” Unlock Premium from $10/mo β†’
CW
Search courses…
πŸ’Ό Servicesℹ️ Aboutβœ‰οΈ ContactView Pricing Plansfrom $10

CROSS JOIN

SQL JoinsJoins🟒 Free Lesson

Advertisement

SQL Joins

CROSS JOIN

Every row from one table paired with every row from another β€” the Cartesian product.

  • No Join Condition β€” pairs every combination without filtering
  • Row Explosion β€” result size is A Γ— B rows
  • Combinatorial Power β€” ideal for generating test data and variant catalogs Unleash every possible pairing.

What Is a CROSS JOIN?

DfCROSS JOIN

A CROSS JOIN returns the Cartesian product of two tables. Every row from the first table is paired with every row from the second table. If table A has M rows and table B has N rows, the result contains M Γ— N rows. No ON clause is used.

Cartesian Product: Table A (3) x Table B (4) = 12 RowsTable A (3)A1A2A3Table B (4)B1B2B3B4Result: 3 x 4 = 12 rows(A1,B1)(A1,B2)(A1,B3)(A1,B4)(A2,B1)(A2,B2)(A2,B3)(A2,B4)(A3,B1)(A3,B2)(A3,B3)(A3,B4)Every A paired with every B β€” no filterWarning: Row explosion on large tables! 10K x 10K = 100M rows
SELECT *
FROM table_a
CROSS JOIN table_b;

CROSS JOINs on large tables produce enormous result sets. A 10,000-row table crossed with itself yields 100,000,000 rows. Always filter or use small tables.

Syntax

-- Explicit CROSS JOIN syntax
SELECT columns
FROM table_a
CROSS JOIN table_b;

-- Implicit syntax (comma-separated)
SELECT columns
FROM table_a, table_b;
Syntax StyleExampleNotes
CROSS JOINFROM a CROSS JOIN bExplicit, preferred
CommaFROM a, bImplicit, less readable
MultipleFROM a CROSS JOIN b CROSS JOIN cChain for 3+ tables

How It Works

Table A RowsTable B RowsResult Rows
3412
10010010,000
1,0001,0001,000,000
10,00010,000100,000,000

Basic Example

-- Pair every customer with every product
SELECT
    c.first_name,
    c.last_name,
    p.product_name
FROM customers c
CROSS JOIN products p
ORDER BY c.first_name, p.product_name;

Generating Product Variants

-- Generate all size Γ— color combinations for a product catalog
SELECT
    p.product_name,
    s.size_name,
    c.color_name,
    CONCAT(p.product_name, ' - ', s.size_name, ' - ', c.color_name) AS variant_name
FROM products p
CROSS JOIN sizes s
CROSS JOIN colors c
ORDER BY p.product_name, s.size_name, c.color_name;

CROSS JOIN is the standard approach for generating all possible attribute combinations in e-commerce catalogs, testing matrices, and reporting templates.

Creating Test Data

-- Populate a user_permissions table with all user Γ— permission pairs
INSERT INTO user_permissions (user_id, permission_id, granted)
SELECT
    u.user_id,
    p.permission_id,
    FALSE
FROM users u
CROSS JOIN permissions p
WHERE NOT EXISTS (
    SELECT 1
    FROM user_permissions up
    WHERE up.user_id = u.user_id
      AND up.permission_id = p.permission_id
);

CROSS JOIN with WHERE (Filtered Cross Join)

-- A CROSS JOIN with a WHERE clause can behave like an INNER JOIN
SELECT
    c.first_name,
    c.last_name,
    o.order_id
FROM customers c
CROSS JOIN orders o
WHERE c.id = o.customer_id;

-- Equivalent to:
SELECT
    c.first_name,
    c.last_name,
    o.order_id
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id;
ApproachWhen to Use
CROSS JOIN + WHEREWhen the filtering condition is complex or non-equi
INNER JOIN + ONStandard practice for equi-joins (preferred)

Practical Use Cases

1. Calendar and Date Matrix

-- Generate a daily sales report for every product Γ— every day in January
SELECT
    d.date_value,
    p.product_name,
    COALESCE(SUM(s.quantity), 0) AS total_sold
FROM (
    SELECT DATE '2024-01-01' + n AS date_value
    FROM generate_series(0, 30) AS n
) d
CROSS JOIN products p
LEFT JOIN sales s
    ON s.sale_date = d.date_value
   AND s.product_id = p.product_id
GROUP BY d.date_value, p.product_name
ORDER BY d.date_value, p.product_name;

2. Comparison Reports

-- Compare each employee's salary against every other employee
SELECT
    e1.first_name AS employee1,
    e1.salary AS salary1,
    e2.first_name AS employee2,
    e2.salary AS salary2,
    e1.salary - e2.salary AS difference
FROM employees e1
CROSS JOIN employees e2
WHERE e1.employee_id < e2.employee_id
  AND ABS(e1.salary - e2.salary) > 10000
ORDER BY difference DESC;

3. Pivot-Style Reports

-- Generate a month Γ— product matrix with zeros for empty cells
SELECT
    m.month_name,
    p.product_name,
    COALESCE(SUM(o.total_amount), 0) AS revenue
FROM (
    SELECT 'January' AS month_name, 1 AS month_num UNION ALL
    SELECT 'February', 2 UNION ALL
    SELECT 'March', 3
) m
CROSS JOIN products p
LEFT JOIN orders o
    ON MONTH(o.order_date) = m.month_num
   AND o.product_id = p.product_id
GROUP BY m.month_name, p.product_name
ORDER BY m.month_num, p.product_name;

Performance Considerations

RiskMitigation
Row explosionLimit source tables or add WHERE early
Memory pressureUse temporary tables for intermediate results
No index usageCROSS JOIN ignores indexes β€” no join predicate exists
Slow queriesAlways EXPLAIN before running on production data
-- Always check result size before executing
SELECT COUNT(*) FROM table_a;  -- M rows
SELECT COUNT(*) FROM table_b;  -- N rows
-- Result will be M Γ— N β€” proceed with caution

Practice Exercises

Exercise 1: Generate all combinations of 3 sizes and 5 colors.

-- Solution
SELECT
    s.size_name,
    c.color_name
FROM sizes s
CROSS JOIN colors c
ORDER BY s.size_name, c.color_name;
-- Expected: 15 rows

Exercise 2: Create a report showing every product alongside every month of 2024, with zero sales where no data exists.

-- Solution
SELECT
    p.product_name,
    m.month_name,
    COALESCE(SUM(o.total_amount), 0) AS monthly_revenue
FROM products p
CROSS JOIN (
    SELECT 'Jan' AS month_name, 1 AS m UNION ALL SELECT 'Feb', 2 UNION ALL
    SELECT 'Mar', 3 UNION ALL SELECT 'Apr', 4 UNION ALL
    SELECT 'May', 5 UNION ALL SELECT 'Jun', 6
) m
LEFT JOIN orders o
    ON o.product_id = p.product_id
   AND MONTH(o.order_date) = m.m
GROUP BY p.product_name, m.month_name, m.m
ORDER BY p.product_name, m.m;

Exercise 3: Generate a test dataset assigning every role to every user.

-- Solution
INSERT INTO user_roles (user_id, role_id, assigned_at)
SELECT
    u.user_id,
    r.role_id,
    NOW()
FROM users u
CROSS JOIN roles r
WHERE NOT EXISTS (
    SELECT 1 FROM user_roles ur
    WHERE ur.user_id = u.user_id
      AND ur.role_id = r.role_id
);

Key Takeaways

  1. CROSS JOIN produces the Cartesian product β€” every row paired with every other row.
  2. The result size is M Γ— N where M and N are the row counts of each table.
  3. No ON clause is needed; all combinations are returned.
  4. Use CROSS JOIN for generating combinations, test data, and pivot-style reports.
  5. Always check table sizes before executing to avoid row explosion.

See Also

⭐

Premium Content

CROSS JOIN

Unlock this lesson and 900+ advanced tutorials with a Premium plan.

🎯End-to-end Projects
πŸ’ΌInterview Prep
πŸ“œCertificates
🀝Community Access

Already a member? Log in

Need Expert SQL Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement