CROSS JOIN在SQL Server中易OOM,因其不预判行数、无视索引与统计信息,强制全表扫描并生成全部组合(如10万×10万=100亿行),内存授予不足时触发RESOURCE_SEMAPHORE等待或被系统杀进程。

CROSS JOIN 本身不报内存不足,但结果集一落地就炸——因为 SQL Server 默认要把整个笛卡尔积加载进内存做后续处理(比如 GROUP BY、ORDER BY 或 INSERT INTO)。
为什么CROSS JOIN在SQL Server里容易触发OOM
不是语法错,是执行模型决定的:SQL Server 对 CROSS JOIN 不做行数预判,只要两表各 10 万行,中间结果就是 100 亿行,而 query_memory_grant 是按估算值分配的,估算偏差大时,实际需要的内存远超授予量,直接触发 RESOURCE_SEMAPHORE 等待或被 Windows 杀进程。
- 错误现象常表现为:
OutOfMemoryException(SSMS 报)、Query timeout expired(应用层)、或 SQL Server 日志里出现hash_warning/sort_warning事件 - 真实业务表(如
users×orders)带索引、约束、统计信息,但CROSS JOIN完全无视这些,强制全表扫描+全内存拼接 - 即使加了
WHERE过滤,如果写在CROSS JOIN之后(即放在ON或WHERE子句末尾),优化器仍可能先算完笛卡尔积再过滤——执行计划里能看到Compute Scalar节点压在最底层
用 VALUES 构造驱动表,彻底绕开业务表
核心原则:不碰 users、orders 这类真实表,只用轻量虚拟数据驱动组合逻辑。SQL Server 支持 VALUES 表值构造器,比临时表更干净、无锁、不写日志。
- 生成固定月份+客户类型组合(比如 12 个月 × 3 类客户):
SELECT m.month_name, c.customer_type FROM (VALUES ('Jan'), ('Feb'), ('Mar')) AS m(month_name) CROSS JOIN (VALUES ('VIP'), ('Normal'), ('Trial')) AS c(customer_type); - 若需数值序列(如 1~1000),可用
master..spt_values(仅限开发/测试)或递归 CTE(注意控制MAXRECURSION):WITH nums AS ( SELECT 1 AS n UNION ALL SELECT n + 1 FROM nums WHERE n < 1000 ) SELECT * FROM nums CROSS JOIN (VALUES ('A'), ('B')) AS t(val); - 禁止把
VALUES和大表混用:比如SELECT * FROM (VALUES ...) AS v CROSS JOIN big_table—— 只要big_table行数不可控,风险照旧
真要连业务表?先收缩输入集再交叉
极少数场景必须基于真实维度(如所有活跃用户 × 所有产品类别),那就得提前切片,不能让 SQL Server 自己硬扛。
- 用子查询显式限制左侧行数:
SELECT u.name, p.category FROM (SELECT TOP 1000 id, name FROM users WHERE status = 'active' ORDER BY id) AS u CROSS JOIN (SELECT DISTINCT category FROM products WHERE is_on_sale = 1) AS p;
- 确保子查询能走索引:
users(status, id)和products(is_on_sale, category)复合索引必须存在,否则TOP或DISTINCT会触发排序溢出到 tempdb - 避免
ORDER BY+OFFSET分页:SQL Server 的OFFSET在大数据集上性能断崖式下跌,改用键值分页(WHERE id > @last_id) - 如果最终要插入结果,别用
INSERT INTO ... SELECT ... CROSS JOIN,改用应用层循环拉取小批次,每批控制在 1 万行以内
SSMS 显示结果时的内存陷阱
即使查询本身没 OOM,SSMS 也可能崩——它是 32 位进程,最多用 2 GB 用户态内存,且网格模式对每字段限 64 KB 文本。
- 运行前务必右键 → 结果到文本(
Results to Text),比网格省 80% 内存 - 若还要导出,选 结果到文件(
Results to File),完全绕过 SSMS 内存缓冲 - 生产环境排查时,直接用
sqlcmd -S server -d db -Q "..." -o output.txt,64 位工具无此限制 - 别信“我只查了 10 行”,只要
CROSS JOIN输出列含XML、TEXT或长VARCHAR,单行就能吃掉几百 MB
最容易被忽略的一点:CROSS JOIN 的危险性不在语法本身,而在它彻底屏蔽了优化器的干预能力——没有 ON 条件,就没有索引可选,没有统计信息可参考,也没有谓词下推空间。一旦写进存储过程或报表脚本,下次数据量翻倍,故障就会准时出现。

















