在关系型数据库里,CROSS JOIN用来返回两张表所有行的两两组合,结果集行数等于左表行数乘以右表行数,这就是笛卡尔积。它常被视作危险操作,因为稍不注意就会生成海量中间数据。但只要理解其执行机制并提前做资源预估,CROSS JOIN也能在不少合理场景中提升写法清晰度。

一、CROSS JOIN的底层执行与内存模型
数据库在执行CROSS JOIN时,通常不会先物化全量笛卡尔积再过滤,优化器可能把它改写成嵌套循环:对外表每一行,扫描内表全部行。但若内表无法放入内存,就会使用临时文件或哈希表溢写磁盘。我们预估资源时,最保守的算法是假设峰值内存等于乘积行数乘单行平均字节数。
例如左表十万行、右表一千行,乘积一亿行。若每行拼接后约200字节,理论峰值约20GB。多数实例的会话工作内存远小于此,于是数据库不得不用临时段,查询延迟陡增。因此动手前先问自己:两表参与连接的行数各是多少,能否先聚合或过滤。
-- 先缩小两表规模再 CROSS JOIN,控制笛卡尔积 WITH small_a AS ( SELECT id, name FROM users WHERE status = 1 LIMIT 1000 ), small_b AS ( SELECT sku_id, price FROM products WHERE cat = 5 LIMIT 500 ) SELECT a.id, b.sku_id FROM small_a a CROSS JOIN small_b b;
二、适合使用笛卡尔积的真实场景
第一种常见场景是小维度表展开。比如把店铺表和日期维表做CROSS JOIN,生成某时间段每个店铺每天的空跑道,再左连接业务表补数据。由于日期维表可能仅数百行,店铺数千行,乘积仍在百万级,内存可控。
第二种是参数矩阵生成。在报表中需要把指标A的若干个阈值与指标B的若干个区间两两组合输出,用CROSS JOIN比写多层UNION更直观。下面示例用两个派生小表生成九种组合,用于后续CASE计算。
WITH thr AS (SELECT 0 AS t UNION ALL SELECT 1 UNION ALL SELECT 2),
seg AS (SELECT 'low' AS s UNION ALL SELECT 'mid' UNION ALL SELECT 'high')
SELECT t.s, s.s AS seg_name
FROM thr t
CROSS JOIN seg s;
三、内存资源预估与调优手段
做预估时建议建一张测算表:左表行数、右表行数、预估单行宽度、乘积行数、期望内存上限。若乘积超过千万,应检查是否能用LATERAL把右表变成依赖左表参数的子集,从而把无条件笛卡尔积降级为受控的一对多。
在PostgreSQL中可设置会话级work_mem,让哈希或排序尽量在内存完成;在MySQL则应关注tmp_table_size与max_heap_table_size。同时开启资源监控,观察临时文件增长。下表给出不同规模下的参考。
| 左表行数 | 右表行数 | 单行宽度(字节) | 乘积行数 | 理论内存 |
|---|---|---|---|---|
| 1,000 | 500 | 120 | 500,000 | 约60MB |
| 100,000 | 1,000 | 200 | 100,000,000 | 约20GB |
| 10,000 | 10,000 | 80 | 100,000,000 | 约8GB |
四、用LATERAL规避全量笛卡尔积
当右表内容依赖左表字段时,可用CROSS JOIN LATERAL(或PostgreSQL的LATERAL、MySQL的派生LATERAL)。它对外表每行执行一次子查询,子查询内部可用左表值过滤,实际配对数量远小于乘积。
如下示例,对每位用户取其一小时内的最近订单,而不是用户表与订单表全连接。这样把潜在几十亿组合压到用户数乘少量订单,内存平稳。
SELECT u.id, o.order_id, o.created_at
FROM users u
CROSS JOIN LATERAL (
SELECT order_id, created_at
FROM orders o
WHERE o.user_id = u.id
AND o.created_at >= u.last_active - INTERVAL '1 hour'
LIMIT 5
) o;
五、写SQL时的检查清单
在提交含CROSS JOIN的语句前,确认是否真的需要两两组合,是否能把任一侧先WHERE或聚合。用EXPLAIN看计划里是否出现Materialize或Hash Join且预估行数异常高。
若必须在大数据集上做笛卡尔积,考虑分批、用临时表落盘、或在应用层用流式处理代替单条SQL。安全使用CROSS JOIN的核心,是让乘积规模落在实例资源边界内,并以业务语义证明其必要性。
SQLCROSS_JOIN笛卡尔积修改时间:2026-08-07 04:33:25