我正在使用Oracle数据库10g 10.2.0.5.0版。我有这样的看法:
CREATE OR REPLACE VIEW some_view
(
A,
B
)
AS
SELECT A, B
FROM table_a
WHERE condition_a
UNION ALL
SELECT A, B
FROM table_b
WHERE condition_b;
和一些数据库函数some_db_package.foo()
。我的问题是当我执行查询时:
SELECT A, some_db_package.foo(B) val
FROM some_view
WHERE some_db_package.foo(B) = 0;
Oracle正在从查询和some_view
合并条件,所以我得到的结果是:
SELECT A, some_db_package.foo(B) val
FROM table_a
WHERE some_db_package.foo(B) = 0 AND condition_a
UNION ALL
SELECT A, some_db_package.foo(B) val
FROM table_b
WHERE some_db_package.foo(B) = 0 AND condition_b;
some_db_package.foo()
在table_a
和table_b
的所有行上执行,我希望仅在过滤后执行some_db_package.foo()
(condition_a
和{{1}行)。假设我不能在查询中使用优化器提示,有没有办法做到这一点(即通过更改sql查询或condition_b
定义)?
问题解决了。总结一下:
some_view
- 对于给定的事件和日期范围计算事件在日期(foo()访问表之间发生的错误),因此仅在some_db_package.foo()
时才是确定性的。
sysdate > dateTo
没有任何区别。
实际上我不需要select * from ( SELECT A, some_db_package.foo(B) val FROM some_view )
而我使用UNION ALL
进行测试,但结果相同。
UNION
没有任何区别。
我使用优化器提示进行测试,不幸的是Oracle忽略了它们。
在with some_view_set as (select A, B from some_view) select * from ( select A, some_db_package.foo(B) val from some_view_set ) where val = 0
中使用ROWNUM >= 1
是解决我问题的方法。
感谢您的帮助,我真的很感激。
答案 0 :(得分:2)
ROWNUM
通常是停止优化程序转换的最佳方法。提示很难正确 - 语法很奇怪且有问题,并且有许多潜在的转换需要停止。还有其他方法可以重新编写查询,但ROWNUM
通常是最好的方法,因为它是以这种方式记录的。 ROWNUM
必须评估最后用于前N个查询,您始终可以依赖它来防止合并查询块。
示例架构
drop table table_a;
drop table table_b;
create table table_a(a number, b number);
create table table_b(a number, b number);
insert into table_a select level, level from dual connect by level <= 10;
insert into table_b select level, level from dual connect by level <= 10;
begin
dbms_stats.gather_table_stats(user, 'table_a');
dbms_stats.gather_table_stats(user, 'table_b');
end;
/
--FOO takes 1 second each time it is executed.
create or replace function foo(p_value number) return number is
begin
dbms_lock.sleep(1);
return 0;
end;
/
--BAR is fast, but the optimizer doesn't know it.
create or replace function bar(p_value number) return number is
begin
return p_value;
end;
/
--This view returns 2 rows.
CREATE OR REPLACE VIEW some_view AS
SELECT A, B
FROM table_a
WHERE a = bar(1)
UNION ALL
SELECT A, B
FROM table_b
WHERE a = bar(2);
查询缓慢
此查询需要20秒才能运行,这意味着该功能将被评估20次。
SELECT A, foo(B) val
FROM some_view
WHERE foo(B) = 0;
解释计划显示条件已合并,并且似乎从左到右评估条件(但不依赖于此始终为真!)。
explain plan for
SELECT A, foo(B) val
FROM some_view
WHERE foo(B) = 0;
select * from table(dbms_xplan.display);
Plan hash value: 4139878329
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 5 (0)| 00:00:01 |
| 1 | VIEW | SOME_VIEW | 1 | 6 | 5 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | TABLE ACCESS FULL| TABLE_A | 1 | 6 | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| TABLE_B | 1 | 6 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("FOO"("B")=0 AND "A"="BAR"(1))
4 - filter("FOO"("B")=0 AND "A"="BAR"(2))
Note
-----
- automatic DOP: skipped because of IO calibrate statistics are missing
快速查询
添加一个看似多余的ROWNUM
谓词,除了阻止转换之外什么都不做。
CREATE OR REPLACE VIEW some_view2 AS
SELECT A, B
FROM table_a
WHERE a = bar(1)
AND ROWNUM >= 1 --Prevent optimizer transformations, for performance.
UNION ALL
SELECT A, B
FROM table_b
WHERE a = bar(2)
AND ROWNUM >= 1 --Prevent optimizer transformations, for performance.
;
现在查询只需要4秒,该功能只运行4次。
SELECT A, foo(B) val
FROM some_view2
WHERE foo(B) = 0;
在新的解释计划中,很明显,在大部分过滤完成后,FOO
函数会被最后评估。
explain plan for
SELECT A, foo(B) val
FROM some_view2
WHERE foo(B) = 0;
select * from table(dbms_xplan.display);
Plan hash value: 4228269064
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 52 | 6 (0)| 00:00:01 |
|* 1 | VIEW | SOME_VIEW2 | 2 | 52 | 6 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
| 3 | COUNT | | | | | |
|* 4 | FILTER | | | | | |
|* 5 | TABLE ACCESS FULL| TABLE_A | 1 | 6 | 3 (0)| 00:00:01 |
| 6 | COUNT | | | | | |
|* 7 | FILTER | | | | | |
|* 8 | TABLE ACCESS FULL| TABLE_B | 1 | 6 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("FOO"("B")=0)
4 - filter(ROWNUM>=1)
5 - filter("A"="BAR"(1))
7 - filter(ROWNUM>=1)
8 - filter("A"="BAR"(2))
Note
-----
- automatic DOP: skipped because of IO calibrate statistics are missing
Ben想要创建函数DETERMINISTIC
的想法也可能有助于减少函数调用。