奇妙に聞こえるかもしれませんが、この特定のケースでは、2 つの組み合わせたクロス結合が役立ちます。
以下の例を見てください。
サンプル データ テーブル:
select * from all_tables;
drop table Purchases;
create table Purchases as
select zx.object_id + (lev-1) * 100000 purchase_id,
object_name purchase_name,
round( dbms_random.value( 1, 200 )) purchase_price,
zx.*
from all_objects zx
cross join (select level lev from dual connect by level <= 170);
create unique index purchases_id_ix on Purchases( Purchase_id );
exec dbms_stats.gather_table_stats( user, 'Purchases' );
select count(*) from Purchases;
COUNT(*)
----------
10316620
クエリ:
var Purchase_id varchar2( 4000 )
var Purchase_name varchar2( 4000 )
var Purchase_price varchar2( 4000 )
begin
:Purchase_id := '1139';
:Purchase_name := NULL;
:Purchase_price := NULL;
end;
/
explain plan for
select p.*
from Purchases p
cross join (
select 1 from dual d
where :Purchase_id is not null
) part_1
where Purchase_id = to_number( :Purchase_id )
and ( :Purchase_name is null or Purchase_name = :Purchase_name )
and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
union all
select p.*
from Purchases p
cross join (
select 1 from dual d
where :Purchase_id is null
) part_2
where
( :Purchase_name is null or Purchase_name = :Purchase_name )
and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
;
説明計画:
Plan hash value: 460094106
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 1 | NESTED LOOPS | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 2 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 3 | VIEW | VW_JF_SET$96C1679A | 28259 | 5546K| 54091 (1)| 00:10:50 |
| 4 | UNION-ALL | | | | | |
|* 5 | FILTER | | | | | |
|* 6 | TABLE ACCESS BY INDEX ROWID| PURCHASES | 1 | 132 | 3 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | PURCHASES_ID_IX | 1 | | 2 (0)| 00:00:01 |
|* 8 | FILTER | | | | | |
|* 9 | TABLE ACCESS FULL | PURCHASES | 28258 | 3642K| 54088 (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter(:PURCHASE_ID IS NOT NULL)
6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
8 - filter(:PURCHASE_ID IS NULL)
9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
27 wierszy zosta│o wybranych.
:Purchase_id <> NULL のテスト
SQL> set pagesize 0
SQL> set linesize 200
SQL> set timing on
SQL> set autotrace traceonly
SQL>
SQL> begin
2 :Purchase_id := '163027';
3 :Purchase_name := NULL;
4 :Purchase_price := NULL;
5 end;
6 /
Procedura PL/SQL zosta│a zako˝czona pomyťlnie.
Ca│kowity: 00:00:00.00
SQL> select p.*
2 from Purchases p
3 cross join (
4 select 1 from dual d
5 where :Purchase_id is not null
6 ) part_1
7 where Purchase_id = to_number( :Purchase_id )
8 and ( :Purchase_name is null or Purchase_name = :Purchase_name )
9 and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
10 union all
11 select p.*
12 from Purchases p
13 cross join (
14 select 1 from dual d
15 where :Purchase_id is null
16 ) part_2
17 where
18 ( :Purchase_name is null or Purchase_name = :Purchase_name )
19 and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
20 ;
Ca│kowity: 00:00:00.09
Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 1 | NESTED LOOPS | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 2 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 3 | VIEW | VW_JF_SET$96C1679A | 28259 | 5546K| 54091 (1)| 00:10:50 |
| 4 | UNION-ALL | | | | | |
|* 5 | FILTER | | | | | |
|* 6 | TABLE ACCESS BY INDEX ROWID| PURCHASES | 1 | 132 | 3 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | PURCHASES_ID_IX | 1 | | 2 (0)| 00:00:01 |
|* 8 | FILTER | | | | | |
|* 9 | TABLE ACCESS FULL | PURCHASES | 28258 | 3642K| 54088 (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter(:PURCHASE_ID IS NOT NULL)
6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
8 - filter(:PURCHASE_ID IS NULL)
9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
Statystyki
----------------------------------------------------------
1 recursive calls
0 db block gets
4 consistent gets
2 physical reads
0 redo size
1865 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
:Purchase_id = NULL のテスト
SQL> begin
2 :Purchase_id := NULL;
3 :Purchase_name := 'DBMS_CUBE_UTIL';
4 :Purchase_price := NULL;
5 end;
6 /
Procedura PL/SQL zosta│a zako˝czona pomyťlnie.
Ca│kowity: 00:00:00.00
SQL> select p.*
2 from Purchases p
3 cross join (
4 select 1 from dual d
5 where :Purchase_id is not null
6 ) part_1
7 where Purchase_id = to_number( :Purchase_id )
8 and ( :Purchase_name is null or Purchase_name = :Purchase_name )
9 and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
10 union all
11 select p.*
12 from Purchases p
13 cross join (
14 select 1 from dual d
15 where :Purchase_id is null
16 ) part_2
17 where
18 ( :Purchase_name is null or Purchase_name = :Purchase_name )
19 and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
20 ;
510 wierszy zosta│o wybranych.
Ca│kowity: 00:00:11.90
Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 1 | NESTED LOOPS | | 28259 | 5546K| 54093 (1)| 00:10:50 |
| 2 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 3 | VIEW | VW_JF_SET$96C1679A | 28259 | 5546K| 54091 (1)| 00:10:50 |
| 4 | UNION-ALL | | | | | |
|* 5 | FILTER | | | | | |
|* 6 | TABLE ACCESS BY INDEX ROWID| PURCHASES | 1 | 132 | 3 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | PURCHASES_ID_IX | 1 | | 2 (0)| 00:00:01 |
|* 8 | FILTER | | | | | |
|* 9 | TABLE ACCESS FULL | PURCHASES | 28258 | 3642K| 54088 (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter(:PURCHASE_ID IS NOT NULL)
6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
8 - filter(:PURCHASE_ID IS NULL)
9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
(:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
Statystyki
----------------------------------------------------------
0 recursive calls
0 db block gets
197993 consistent gets
82655 physical reads
0 redo size
16506 bytes sent via SQL*Net to client
882 bytes received via SQL*Net from client
35 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
510 rows processed
実際の実行時間を知るには、計画を見ないでください。計画は嘘をつき、見積もりのみを含みます (オラクルがどのように考えるか)。「Ca│kowity」の行を見てください。これは「合計実行時間」を意味します (sqlplus でコードページを英語に変更する方法がわかりません)。「一貫した取得」も見てください。これは、クエリが読み取る論理的な一貫性のあるブロックの数です。
最初のクエリ (purchase_id <> null )
Ca│kowity: 00:00:00.09
4 consistent gets
2 physical reads
明らかにインデックスを使用しており、時間は 90 ミリ秒です
。2 番目のクエリ (purchase_id = null )
Ca│kowity: 00:00:11.90
197993 consistent gets
82655 physical reads
このクエリは全テーブル スキャンを実行します。