如何使用 Oracle 提示或其他优化来修复函数在 where 子句中的性能问题?

How to use Oracle hints or other optimization to fix function in where clause performance issue?

这很慢:

select col_x from table_a where col_y in (select col_y from table_b) and fn(col_x)=0;

但我知道这将 return 快速处理 4 行,而且我可以 运行 fn() 快速处理 4 个值。

所以我做了一些测试,我发现这很快:

select fn(col_x) from table_a where col_y in (select col_y from table_b);

在 where 子句中使用 fn() 时,Oracle 运行在 table_a 的每一行上使用它。我怎样才能使 Oracle 首先使用 col_y 过滤器,并且仅 运行 匹配行上的函数?

例如,从概念上讲,我认为这可行:

with taba as (
   select fn(col_x) x from table_a where col_y in (select col_y from table_b)
)
select * from taba where x=0;

因为我认为 Oracle 会先 运行 with 子句,但 Oracle "optimizing" 这个查询并使这个 运行 与上面的第一个查询完全相同 where fn(col_x)=0在where子句中。

我希望 运行 只是作为查询而不是在 pl/sql 块中。似乎应该有办法给 oracle 一个提示,或者做一些其他的把戏,但我想不通。顺便说一句,table 在 col_y 上建立索引,它被用作访问谓词。统计数据是最新的。

这有效,但如果有人有更好的答案,请分享:

select col_x
from table_a 
where col_y in (select col_y from table_b) 
  and (select 1 from dual where fn(col_x)=0);

有点笨拙,但有效。将查询 运行 在 60 秒以上缩短到 0.1 秒。

您可以在查询中尝试使用 HAVING 子句。在基本查询完成之前不会执行此子句,然后 HAVING 子句在结果行上是 运行。它通常用于分析函数,但在您的情况下可能会有用。

select col_x 
from table_a 
where col_y in (select col_y from table_b) 
having fn(col_x)=0;

A HAVING clause restricts the results of a GROUP BY in a SelectExpression. The HAVING clause is applied to each group of the grouped table, much as a WHERE clause is applied to a select list. If there is no GROUP BY clause, the HAVING clause is applied to the entire result as a single group. The SELECT clause cannot refer directly to any column that does not have a GROUP BY clause. It can, however, refer to constants, aggregates, and special registers.

http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj14854.html

有两种方法可以绕过它,

1) 在子查询中添加'AND rownum >=0'强制实现。

2) 在查询中使用 Case 语句来强制执行优先级(可能)

1) 为什么不尝试使用 col_y.

加入 table_a 和 table_b
  select a.col_x from table_a a,table_b b
    where a.col_y = b.col_y
     and fn(col_x) = 0 

2) NO_PUSH_PRED -

 select /*+ NO_PUSH_PRED(v) */ col_x from (
    select col_x from table_a  where col_y in (select col_y from table_b) 
   ) v 
   where fn(col_x) =0

3) 存在并且 PUSH_SUBQ.

select col_x from table_a a 
    where exists( select /*+ PUSH_SUBQ */ 1  from table_b  b where a.col_y = b.coly )
     and fn(col_x) = 0;