如何使用 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;
这很慢:
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;