过滤器操作可以帮助使用静态 SQL 构建动态查询。特别是当列列表是静态的时。
您可能已经考虑过这种方法,但出于性能原因放弃了它。“如果我们只需要其中一个的结果,为什么要执行每个 SQL 块?” 你很幸运,优化器已经为你做了一个FILTER
操作。
示例查询
首先创建一个每次运行等待 5 秒的函数。它将帮助查找执行了哪些查询块。
create or replace function slow_function return number is begin
dbms_lock.sleep(5);
return 1;
end;
/
此静态查询由绑定变量控制。有三个查询块,但整个查询在 5 秒内运行,而不是 15 秒。
declare
v_sum number;
v_query1 number := 1;
v_query2 number := 0;
v_query3 number := 0;
begin
select sum(total)
into v_sum
from
(
select total from (select slow_function() total from dual) where v_query1 = 1
union all
select total from (select slow_function() total from dual) where v_query2 = 1
union all
select total from (select slow_function() total from dual) where v_query3 = 1
);
end;
/
执行计划
这种表现不是好运的结果;这不仅仅是 Oracle 在另一个谓词之前随机执行一个谓词。Oracle 在运行前分析绑定变量,甚至不执行不相关的查询块。这就是FILTER
下面的操作正在做的事情。(这是一个糟糕的名字,许多人通常将所有谓词称为“过滤器”。但只有其中一些会导致FILTER
操作。)
select * from table(dbms_xplan.display_cursor(sql_id => '0cfqc6a70kzmt'));
SQL_ID 0cfqc6a70kzmt, child number 0
-------------------------------------
SELECT SUM(TOTAL) FROM ( SELECT TOTAL FROM (SELECT SLOW_FUNCTION()
TOTAL FROM DUAL) WHERE :B1 = 1 UNION ALL SELECT TOTAL FROM (SELECT
SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B2 = 1 UNION ALL SELECT TOTAL
FROM (SELECT SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B3 = 1 )
Plan hash value: 926033116
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 6 (100)| |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | VIEW | | 3 | 39 | 6 (0)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
|* 4 | FILTER | | | | | |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
|* 6 | FILTER | | | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
|* 8 | FILTER | | | | | |
| 9 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter(:B1=1)
6 - filter(:B2=1)
8 - filter(:B3=1)
问题
该FILTER
操作记录不充分。我无法详细解释它何时起作用或不起作用,以及它对查询的其他部分的确切影响。例如,在解释计划中Rows
估计为 3,但在运行时 Oracle 应该很容易估计基数为 1。显然执行计划不是那么动态的,糟糕的基数估计可能会导致以后的问题。此外,我还看到了一些奇怪的情况,静态表达式没有被适当地过滤。但是,如果查询使用简单的等式谓词,它应该没问题。
这种方法允许您删除所有动态 SQL 并将其替换为大型静态 SQL 语句。有一些优点;动态 SQL 通常“丑陋”且难以调试。但是只熟悉过程式编程的人倾向于将单个 SQL 语句视为一个巨大的上帝函数,这是一种不好的做法。他们不会欣赏UNION ALL
s 创建独立的 SQL 块
动态 SQL 仍然可能更好
一般来说,我建议不要使用这种方法。你拥有的东西很好,因为它看起来不错。动态 SQL 的主要问题是人们不把它当作真正的代码。它没有评论或格式化,最终看起来像一个没人能理解的可怕混乱。如果您能够花费额外的时间来生成干净的代码,那么您应该坚持下去。