フィルター操作は、静的 SQL を使用して動的クエリを作成するのに役立ちます。特に列リストが静的な場合。
すでにこのアプローチを検討していたかもしれませんが、パフォーマンス上の理由で破棄しました。「そのうちの 1 つの結果だけが必要な場合、なぜすべての SQL ブロックを実行するのでしょうか?」幸運なことに、オプティマイザーはすでにFILTER
操作でこれを行っています。
クエリの例
最初に、実行されるたびに 5 秒待機する関数を作成します。実行されたクエリ ブロックを見つけるのに役立ちます。
create or replace function slow_function return number is begin
dbms_lock.sleep(5);
return 1;
end;
/
この静的クエリは、バインド変数によって制御されます。3 つのクエリ ブロックがありますが、クエリ全体が 15 秒ではなく 5 秒で実行されます。
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 ステートメントを 1 つの巨大な神の関数と考える傾向があり、これは悪い習慣です。彼らは、UNION ALL
s が SQL の独立したブロックを作成することを認めません。
動的 SQL の方がおそらく優れている
一般的に、私はこのアプローチに反対することをお勧めします。見た目がいいから、持っているものがいい。動的 SQL の主な問題は、人々がそれを実際のコードのように扱わないことです。コメントもフォーマットもされておらず、誰も理解できない恐ろしい混乱のように見えます。クリーンなコードを生成するために余分な時間を費やすことができる場合は、それに固執する必要があります。