CROSS JOIN produces a Cartesian product - every row from the first table combined with every row from the second table. LATERAL joins allow correlated subqueries and table functions. Essential for generating combinations, expanding arrays, and complex transformations.
Syntax
CROSS JOIN:
SELECT *
FROM table1
CROSSJOIN
SELECT *
FROM table1
CROSSJOIN
SELECT *
FROM table1
CROSSJOIN
LATERAL (Snowflake):
SELECT *
FROM table1,LATERAL(SELECT * FROM table2 WHERE table2.id = table1.id
)
SELECT *
FROM table1,LATERAL(SELECT * FROM table2 WHERE table2.id = table1.id
)
SELECT *
FROM table1,LATERAL(SELECT * FROM table2 WHERE table2.id = table1.id
)
CROSS JOIN UNNEST (BigQuery):
SELECT *
FROM table1
CROSSJOINUNNEST(array_column)as
SELECT *
FROM table1
CROSSJOINUNNEST(array_column)as
SELECT *
FROM table1
CROSSJOINUNNEST(array_column)as
LATERAL VIEW (Databricks):
SELECT *
FROM table1
LATERALVIEW EXPLODE(array_column) lateral_alias AS
SELECT *
FROM table1
LATERALVIEW EXPLODE(array_column) lateral_alias AS
SELECT *
FROM table1
LATERALVIEW EXPLODE(array_column) lateral_alias AS
Platform-Specific Notes
Snowflake:
Full CROSS JOIN support
LATERAL for correlated table functions
LATERAL FLATTEN for arrays/objects
Can reference columns from left side
BigQuery:
CROSS JOIN for Cartesian product
CROSS JOIN UNNEST for arrays
No LATERAL keyword needed
Implicit lateral joins in subqueries
Databricks:
CROSS JOIN standard
LATERAL VIEW for table-generating functions
LATERAL VIEW EXPLODE for arrays
Compatible with Hive syntax
Example 1: CROSS JOIN - Generate All Combinations
All Platforms:
-- Generate all size-color combinationsSELECT
p.product_name,
s.size,
c.color,
CONCAT(p.product_name,' - ', s.size,' - ', c.color)as variant_name
FROM products p
CROSSJOIN sizes s
CROSSJOIN colors c
ORDERBY p.product_name, s.size,
-- Generate all size-color combinationsSELECT
p.product_name,
s.size,
c.color,
CONCAT(p.product_name,' - ', s.size,' - ', c.color)as variant_name
FROM products p
CROSSJOIN sizes s
CROSSJOIN colors c
ORDERBY p.product_name, s.size,
-- Generate all size-color combinationsSELECT
p.product_name,
s.size,
c.color,
CONCAT(p.product_name,' - ', s.size,' - ', c.color)as variant_name
FROM products p
CROSSJOIN sizes s
CROSSJOIN colors c
ORDERBY p.product_name, s.size,
-- Generate all dates and hours for a weekWITH dates AS(SELECT DATE '2024-01-15' + INTERVAL (seq - 1)DAYas date
FROMUNNEST(GENERATE_ARRAY(1,7))as seq -- BigQuery-- TABLE(GENERATOR(ROWCOUNT => 7)) as seq -- Snowflake-- EXPLODE(SEQUENCE(1, 7)) as seq -- Databricks),
hours AS(SELECT seq ashourFROMUNNEST(GENERATE_ARRAY(0,23))as seq -- BigQuery)SELECT
date,hour,
TIMESTAMP(date) + INTERVAL hourHOURas datetime
FROM dates
CROSSJOIN hours
ORDERBY date,hour
-- Generate all dates and hours for a weekWITH dates AS(SELECT DATE '2024-01-15' + INTERVAL (seq - 1)DAYas date
FROMUNNEST(GENERATE_ARRAY(1,7))as seq -- BigQuery-- TABLE(GENERATOR(ROWCOUNT => 7)) as seq -- Snowflake-- EXPLODE(SEQUENCE(1, 7)) as seq -- Databricks),
hours AS(SELECT seq ashourFROMUNNEST(GENERATE_ARRAY(0,23))as seq -- BigQuery)SELECT
date,hour,
TIMESTAMP(date) + INTERVAL hourHOURas datetime
FROM dates
CROSSJOIN hours
ORDERBY date,hour
-- Generate all dates and hours for a weekWITH dates AS(SELECT DATE '2024-01-15' + INTERVAL (seq - 1)DAYas date
FROMUNNEST(GENERATE_ARRAY(1,7))as seq -- BigQuery-- TABLE(GENERATOR(ROWCOUNT => 7)) as seq -- Snowflake-- EXPLODE(SEQUENCE(1, 7)) as seq -- Databricks),
hours AS(SELECT seq ashourFROMUNNEST(GENERATE_ARRAY(0,23))as seq -- BigQuery)SELECT
date,hour,
TIMESTAMP(date) + INTERVAL hourHOURas datetime
FROM dates
CROSSJOIN hours
ORDERBY date,hour
Result (168 rows = 7 days × 24 hours):
date
hour
datetime
2024-01-15
0
2024-01-15 00:00:00
2024-01-15
1
2024-01-15 01:00:00
...
...
...
2024-01-21
23
2024-01-21 23:00:00
Example 3: LATERAL FLATTEN (Snowflake)
Snowflake:
SELECT
o.order_id,
o.customer_name,
f.value:product_name::STRING as product_name,
f.value:quantity::INTEGER as quantity,
f.value:price::FLOAT as price
FROM orders o,LATERAL FLATTEN(input => o.items)
SELECT
o.order_id,
o.customer_name,
f.value:product_name::STRING as product_name,
f.value:quantity::INTEGER as quantity,
f.value:price::FLOAT as price
FROM orders o,LATERAL FLATTEN(input => o.items)
SELECT
o.order_id,
o.customer_name,
f.value:product_name::STRING as product_name,
f.value:quantity::INTEGER as quantity,
f.value:price::FLOAT as price
FROM orders o,LATERAL FLATTEN(input => o.items)
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price,
item.quantity * item.price as line_total
FROM orders o
CROSSJOINUNNEST(o.items)as
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price,
item.quantity * item.price as line_total
FROM orders o
CROSSJOINUNNEST(o.items)as
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price,
item.quantity * item.price as line_total
FROM orders o
CROSSJOINUNNEST(o.items)as
Result (same as Snowflake example above with added line_total)
Example 5: LATERAL VIEW EXPLODE (Databricks)
Databricks:
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price
FROM orders o
LATERALVIEW EXPLODE(o.items) items_table AS
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price
FROM orders o
LATERALVIEW EXPLODE(o.items) items_table AS
SELECT
o.order_id,
o.customer_name,
item.product_name,
item.quantity,
item.price
FROM orders o
LATERALVIEW EXPLODE(o.items) items_table AS
Example 6: Fill Missing Data with CROSS JOIN
All Platforms:
-- Ensure all products have entries for all dates (fill gaps)WITH date_range AS(SELECT date
FROMUNNEST(GENERATE_DATE_ARRAY('2024-01-01','2024-01-07'))as date
),
all_products AS(SELECTDISTINCT product_id, product_name
FROM products
),
all_combinations AS(SELECT
p.product_id,
p.product_name,
d.date
FROM all_products p
CROSSJOIN date_range d
)SELECT
ac.product_id,
ac.product_name,
ac.date,
COALESCE(s.sales,0)as sales
FROM all_combinations ac
LEFTJOIN sales s
ON ac.product_id = s.product_id
AND ac.date = s.sale_date
ORDERBY ac.product_id,
-- Ensure all products have entries for all dates (fill gaps)WITH date_range AS(SELECT date
FROMUNNEST(GENERATE_DATE_ARRAY('2024-01-01','2024-01-07'))as date
),
all_products AS(SELECTDISTINCT product_id, product_name
FROM products
),
all_combinations AS(SELECT
p.product_id,
p.product_name,
d.date
FROM all_products p
CROSSJOIN date_range d
)SELECT
ac.product_id,
ac.product_name,
ac.date,
COALESCE(s.sales,0)as sales
FROM all_combinations ac
LEFTJOIN sales s
ON ac.product_id = s.product_id
AND ac.date = s.sale_date
ORDERBY ac.product_id,
-- Ensure all products have entries for all dates (fill gaps)WITH date_range AS(SELECT date
FROMUNNEST(GENERATE_DATE_ARRAY('2024-01-01','2024-01-07'))as date
),
all_products AS(SELECTDISTINCT product_id, product_name
FROM products
),
all_combinations AS(SELECT
p.product_id,
p.product_name,
d.date
FROM all_products p
CROSSJOIN date_range d
)SELECT
ac.product_id,
ac.product_name,
ac.date,
COALESCE(s.sales,0)as sales
FROM all_combinations ac
LEFTJOIN sales s
ON ac.product_id = s.product_id
AND ac.date = s.sale_date
ORDERBY ac.product_id,
Sample Data:
sales (incomplete - missing dates):
product_id
sale_date
sales
1
2024-01-01
100
1
2024-01-03
150
2
2024-01-02
200
Result (complete - all dates filled):
product_id
product_name
date
sales
1
Laptop
2024-01-01
100
1
Laptop
2024-01-02
0
1
Laptop
2024-01-03
150
1
Laptop
2024-01-04
0
...
...
...
...
Example 7: Correlated LATERAL Join (Snowflake)
Snowflake:
-- Get top 3 products for each customerSELECT
c.customer_id,
c.customer_name,
top_products.product_name,
top_products.total_spent,
top_products.purchase_count
FROM customers c,LATERAL(SELECT
p.product_name,
SUM(o.total)as total_spent,COUNT(*)as purchase_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.customer_id = c.customer_id
GROUPBY p.product_name
ORDERBY SUM(o.total)DESCLIMIT3) top_products
ORDERBY c.customer_id, top_products.total_spent DESC
-- Get top 3 products for each customerSELECT
c.customer_id,
c.customer_name,
top_products.product_name,
top_products.total_spent,
top_products.purchase_count
FROM customers c,LATERAL(SELECT
p.product_name,
SUM(o.total)as total_spent,COUNT(*)as purchase_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.customer_id = c.customer_id
GROUPBY p.product_name
ORDERBY SUM(o.total)DESCLIMIT3) top_products
ORDERBY c.customer_id, top_products.total_spent DESC
-- Get top 3 products for each customerSELECT
c.customer_id,
c.customer_name,
top_products.product_name,
top_products.total_spent,
top_products.purchase_count
FROM customers c,LATERAL(SELECT
p.product_name,
SUM(o.total)as total_spent,COUNT(*)as purchase_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.customer_id = c.customer_id
GROUPBY p.product_name
ORDERBY SUM(o.total)DESCLIMIT3) top_products
ORDERBY c.customer_id, top_products.total_spent DESC
*dbt® and dbt Core® are federally registered trademarks of dbt Labs, Inc. in the United States and various jurisdictions around the world. Paradime is not a partner of dbt Labs. All rights therein are reserved to dbt Labs. Paradime is not a product or service of or endorsed by dbt Labs, Inc.
*dbt® and dbt Core® are federally registered trademarks of dbt Labs, Inc. in the United States and various jurisdictions around the world. Paradime is not a partner of dbt Labs. All rights therein are reserved to dbt Labs. Paradime is not a product or service of or endorsed by dbt Labs, Inc.
*dbt® and dbt Core® are federally registered trademarks of dbt Labs, Inc. in the United States and various jurisdictions around the world. Paradime is not a partner of dbt Labs. All rights therein are reserved to dbt Labs. Paradime is not a product or service of or endorsed by dbt Labs, Inc.