Cross Join
The Cartesian product join in Azure SQL — every row of one table paired with every row of another.
A CROSS JOIN produces the Cartesian product of two tables: it pairs every row of the
left table with every row of the right table. Unlike INNER JOIN or LEFT JOIN, it has no
ON clause — there is no matching condition, because every combination is returned by definition.
If the left table has M rows and the right table has N rows, the result has M × N rows. Two small tables can multiply into a very large result set, so cross joins are powerful but easy to misuse.
Syntax
Azure SQL (T-SQL) accepts two equivalent forms — the explicit keyword and the older comma syntax:
-- Explicit, preferred
SELECT s.Size, c.Color
FROM Sizes AS s
CROSS JOIN Colors AS c;
-- Legacy comma form (same result, avoid in new code)
SELECT s.Size, c.Color
FROM Sizes AS s, Colors AS c;
Example: generating every product variant
Say a store stocks a few sizes and a few colors, and you want every possible combination as the starting catalog of variants.
CREATE TABLE Sizes (Size varchar(4));
CREATE TABLE Colors (Color varchar(20));
INSERT INTO Sizes VALUES ('S'), ('M'), ('L');
INSERT INTO Colors VALUES ('Black'), ('White');
SELECT s.Size, c.Color
FROM Sizes AS s
CROSS JOIN Colors AS c
ORDER BY s.Size, c.Color;
3 sizes × 2 colors → 6 rows:
| Size | Color | |------|-------| | L | Black | | L | White | | M | Black | | M | White | | S | Black | | S | White |
When cross joins are genuinely useful
- Generating combinations — every size×color, every product×region, every user×feature-flag.
- Building calendars / number series — cross join digit tables (
0-9) together to manufacture a dense range of dates or integers with no gaps. - Seeding test data — multiply a handful of rows into thousands quickly.
- Filling reporting grids — cross join all
Stores × Months, thenLEFT JOINthe actual sales so months with zero sales still appear as0instead of vanishing.
-- A gap-free calendar of the first 1,000 days from a start date
SELECT DATEADD(day, ones.n + 10 * tens.n + 100 * hundreds.n, '2026-01-01') AS TheDate
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS ones(n)
CROSS JOIN (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS tens(n)
CROSS JOIN (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS hundreds(n)
ORDER BY TheDate;
The accidental cross join
The most common way to meet a CROSS JOIN is by accident: forgetting the join predicate. These
two queries return the same explosive result — the comma form just hides the mistake:
-- Meant to match customers to their orders, but the WHERE/ON was forgotten:
SELECT *
FROM Customers c, Orders o; -- 10k customers × 50k orders = 500,000,000 rows
Related: CROSS APPLY
Azure SQL also has CROSS APPLY, a T-SQL cousin. It looks similar but is correlated: the
right side is a table expression (often a function) that is evaluated per left row, so it can
reference columns from the left table — something a plain CROSS JOIN cannot do.
-- For each customer, their 3 most recent orders
SELECT c.Name, o.OrderId, o.OrderDate
FROM Customers AS c
CROSS APPLY (
SELECT TOP (3) OrderId, OrderDate
FROM Orders
WHERE Orders.CustomerId = c.CustomerId -- references the left row
ORDER BY OrderDate DESC
) AS o;