听起来很奇怪,在这种特定情况下,两个组合的交叉连接可以提供帮助。
看下面的例子。
样本数据表:
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
要知道真正的执行时间,不要看计划,他们会撒谎,只包含估计(oracle 认为它会是怎样的)。查看带有“Ca│kowity”的行,它的意思是“总执行时间”(我不知道如何在 sqlplus 中将代码页更改为英文)。另请查看“一致获取”,这是查询读取的许多逻辑一致块。
第一个查询(purchase_id <> null )
Ca│kowity: 00:00:00.09
4 consistent gets
2 physical reads
显然它使用了索引,时间是 90 ms
第二次查询(purchase_id = null )
Ca│kowity: 00:00:11.90
197993 consistent gets
82655 physical reads
此查询执行全表扫描。