Oracle 如何避免在执行计划中合并笛卡尔连接?

Oracle how to avoid merge cartesian join in execution plan?

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

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) return 一个结果。因此假设它将生成很多行。"

有什么方法可以告诉 Oracle 优化器子 select 只会 return 一行吗?

是否会使用 return 日期时间值的函数代替子 select 帮助,假设优化器然后知道该函数只能 return一个值?

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

我不会担心笛卡尔连接,因为子查询只有 returning 一行(最多)。否则,您将收到 "subquery returns too many rows" 错误。

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

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

然而,这个执行计划可能更糟,因为你没有指定 n2 只应该 return (最多)一个值。

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

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

尝试添加 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 看到它时,它会想到 "these rows must be returned in order, I better not do any query transformations"。这意味着它不会尝试推送谓词、合并视图等。这意味着 VIEW_NAME1 将 运行 与 VIEW_NAME2 分开并且它们现在都 运行 一样快和以前一样。

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