Oracle如何避免合并笛卡尔加入执行计划?

时间:2017-12-12 19:32:39

标签: sql oracle join cartesian

以下查询有时会导致执行计划中的合并笛卡尔联接,我们正在尝试重写查询(以最简单的方式),以确保不再发生合并笛卡尔联接。

SELECT COL1 
FROM SCHEMA.VIEW_NAME1 
WHERE DATE_VAL > (SELECT DATE_VAL FROM SCHEMA.VIEW_NAME2)

在审核了类似的问题“Why would this query cause a Merge Cartesian Join in Oracle”后,问题似乎是“ Oracle不知道(SELECT DATE_VAL FROM SCHEMA.VIEW_NAME2)返回一个结果。所以假设它会生成很多行。

有没有办法告诉Oracle优化器子选择只返回一行?

使用一个返回日期时间值的函数来代替子选择帮助,假设优化器会知道该函数只能返回一个值吗?

SELECT COL1 
FROM SCHEMA.VIEW_NAME1
WHERE DATE_VAL > SCHEMA.FN_GET_DATE_VAL()

Oracle DBA建议使用WITH语句,看起来它会起作用,但我们想知道是否有更短的选项。

with mx_dt as (SELECT DATE_VAL FROM SCHEMA.VIEW_NAME2)
SELECT COL1
FROM SCHEMA.VIEW_NAME1, mx_dt a
WHERE DATE_VAL > a.DATE_VAL

3 个答案:

答案 0 :(得分:0)

我不担心笛卡尔连接,因为子查询只返回一行(最多)。否则,你会得到一个"子查询返回太多行"错误。

Oracle可能会为每次比较运行子查询一次 - 可能,但Oracle优化器很聪明,我怀疑会发生这种情况。但是,很容易将其标记为JOIN

SELECT n1.COL1 
FROM SCHEMA.VIEW_NAME1 n1 JOIN
     SCHEMA.VIEW_NAME2 n2
     ON n1.DATE_VAL > n2.DATE_VAL;

但是,执行计划可能更糟糕,因为您没有指定n2仅应返回(最多)一个值。

答案 1 :(得分:0)

子选择中的聚合函数可确保返回单行。对于优化器可能是一个很好的提示,如果VIEW_NAME2中只有一行,那么子选择的结果是相同的。

SELECT COL1 
  FROM SCHEMA.VIEW_NAME1 
  WHERE DATE_VAL > (SELECT MIN(DATE_VAL) FROM SCHEMA.VIEW_NAME2)

答案 2 :(得分:0)

尝试添加WHERE ROWNUM >= 1

SELECT COL1 
FROM SCHEMA.VIEW_NAME1 
WHERE DATE_VAL > (SELECT DATE_VAL FROM SCHEMA.VIEW_NAME2 WHERE ROWNUM >= 1)

该谓词看起来完全无关,或者Oracle会忽略的事情,但ROWNUM伪函数是特殊的。当Oracle看到它时,它认为"这些行必须按顺序返回,我最好不要进行任何查询转换"。这意味着它不会尝试推送谓词,合并视图等。这意味着VIEW_NAME1将与VIEW_NAME2分开运行,现在它们的运行速度与以前一样快。

您可能仍然会在解释计划中看到笛卡尔积,但希望在两个视图结果集之间仅靠近顶部。如果确实只返回了一行,则笛卡尔积可能是正确的操作。