是,执行计划中出现 MERGE JOIN CARTESIAN 即表示发生笛卡尔连接,必伴 BUFFER SORT 操作,常见于子查询缺失关联条件、谓词未下推或统计信息失准导致 CBO 错选路径。

看执行计划里有没有 MERGE JOIN CARTESIAN
Oracle 19c 的执行计划中,只要出现 MERGE JOIN CARTESIAN 操作,就代表发生了笛卡尔连接——哪怕它藏在子查询里。这不是误判,是 CBO 明确选择的连接方式。
-
MERGE JOIN CARTESIAN一定伴随BUFFER SORT(对其中一张表做内存排序缓存),这是关键识别信号 - 它通常出现在子查询未加关联条件、或外层 WHERE 条件未能下推到子查询内部时
- 注意:即使子查询用了
WITH或内联视图(inline view),只要最终执行计划里有该操作,就是笛卡尔
示例中这条语句:
SELECT e.ename, (SELECT d.dname FROM dept d) FROM emp e;如果
dept 表没被外层限制(比如没加 WHERE d.deptno = e.deptno),执行计划就会显示 MERGE JOIN CARTESIAN,因为子查询每次执行都返回全部 dept 行,和 emp 的每一行配对。
用 DBMS_XPLAN.DISPLAY_CURSOR 查真实执行路径
AUTOTRACE 在子查询场景下容易漏掉中间步骤,必须用带绑定变量和实际统计的方案:
- 先查出 SQL 的
sql_id:SELECT sql_id, child_number FROM v$sql WHERE sql_text LIKE '%your_subquery%'; - 再执行:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('abc123xyz', NULL, 'ALLSTATS LAST')); - 关键看
A-Rows列:如果子查询部分的A-Rows是常量(比如始终返回 4 行),但外层驱动了 1000 行,那乘积就是 4000 行——这就是隐式笛卡尔
特别注意:E-Rows(估算行数)若远低于 A-Rows,说明统计信息不准,CBO 错估了子查询结果集大小,也可能诱使它选错连接方式。
子查询写法不当导致笛卡尔的常见模式
不是所有子查询都会触发笛卡尔,但以下写法极易中招:
- 标量子查询(scalar subquery)里漏写关联列,例如:
(SELECT MAX(d.loc) FROM dept d)而非(SELECT MAX(d.loc) FROM dept d WHERE d.deptno = e.deptno) -
EXISTS子查询里用了非关联的IN或硬编码值,如:EXISTS (SELECT 1 FROM dept d WHERE d.deptno IN (10,20,30)) - 使用
WITH定义的子查询未被主查询任何条件过滤,且未加WHERE限制行数 - 外连接(
LEFT JOIN)后跟一个无关联子查询,CBO 可能放弃下推优化,把子查询当作独立结果集全量连接
这些情况不会报错,但执行计划里 MERGE JOIN CARTESIAN 会悄悄出现,且 Buffers 和 Consistent Gets 会指数级上涨。
为什么不能只靠语法检查?
你写了 ON e.deptno = d.deptno,不代表执行时真用了它。Oracle 19c 的 CBO 可能在以下情况下“绕过”连接条件:
- 子查询被标记为
NOT NULL且外层列也NOT NULL,CBO 可能尝试谓词重写,意外消除关联约束 - 统计信息陈旧,让 CBO 认为某张表只返回 1 行(
Rows = 1),从而误判为安全的笛卡尔(参考知识库中“优化器错误地把某个表返回的Rows算为1行”) - 使用了函数索引或虚拟列,而统计信息未覆盖其分布,CBO 对子查询输出行数严重误估
所以,光看 SQL 写法不够,必须结合 DISPLAY_CURSOR 输出的 A-Rows 和 Operation 链路,确认每一步是否真的按你写的逻辑走。
最危险的点往往藏在嵌套三层以上的子查询里:外层看不出异常,但中间一层的 BUFFER SORT 已经在悄悄放大数据量。


















