JWとa_horse_with_no_nameのステートメントをバックアップするために、ここにテストがあります。すべて同じ接続を使用しているため、ここでは接続(プーリング)の問題は無視されます。
まず、主キーを使用してテーブルを作成し、オプティマイザーの統計を収集します。
SQL> create table mytable
2 as
3 select level id
4 , lpad('*',1000,'*') filler
5 from dual
6 connect by level <= 10000
7 /
Table created.
SQL> alter table mytable add constraint my_pk primary key (id)
2 /
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'mytable')
PL/SQL procedure successfully completed.
次に、ウォームアップとしてすべてのステートメントを共有プールに配置し、クエリの出力のほとんどを削減します。
SQL> select * from mytable where id = 1
2 /
ID
----------
FILLER
---------------------------------------------------------------------------------------------------------------------------------------
1
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
*******************************************************
1 row selected.
SQL> select * from mytable where id = 2
2 /
1 row selected.
SQL> select * from mytable where id = 3
2 /
1 row selected.
SQL> select * from mytable where id = 4
2 /
1 row selected.
SQL> select * from mytable where id in (1,2,3,4)
2 /
4 rows selected.
次に、自動トレース出力を調べて、データベースが実行する必要のある作業を確認します。
SQL> set autotrace traceonly
SQL> select * from mytable where id = 1
2 /
1 row selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1005 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 1 | 1005 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | MY_PK | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=1)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1387 bytes sent via SQL*Net to client
448 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from mytable where id = 2
2 /
1 row selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1005 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 1 | 1005 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | MY_PK | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1387 bytes sent via SQL*Net to client
448 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from mytable where id = 3
2 /
1 row selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1005 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 1 | 1005 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | MY_PK | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=3)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1387 bytes sent via SQL*Net to client
448 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from mytable where id = 4
2 /
1 row selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1005 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 1 | 1005 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | MY_PK | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=4)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1387 bytes sent via SQL*Net to client
448 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from mytable where id in (1,2,3,4)
2 /
4 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1637292604
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 4020 | 4 (0)| 00:00:01 |
| 1 | INLIST ITERATOR | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 4 | 4020 | 4 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | MY_PK | 4 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=1 OR "ID"=2 OR "ID"=3 OR "ID"=4)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
2435 bytes sent via SQL*Net to client
448 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
SQL> set autotrace off
4回3回の一貫した取得と8回の一貫した取得。最後に、すべてのステートメントを10,000回実行して、両方のバリアントの時間を計りましょう。
SQL> set timing on
SQL> declare
2 v_id number;
3 v_filler varchar2(1000);
4 begin
5 for i in 1 .. 10000
6 loop
7 select *
8 into v_id, v_filler
9 from mytable
10 where id = 1
11 ;
12 select *
13 into v_id, v_filler
14 from mytable
15 where id = 2
16 ;
17 select *
18 into v_id, v_filler
19 from mytable
20 where id = 3
21 ;
22 select *
23 into v_id, v_filler
24 from mytable
25 where id = 4
26 ;
27 end loop;
28 end;
29 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.03
SQL> /
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.00
SQL> /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.99
SQL> declare
2 v_id number;
3 v_filler varchar2(1000);
4 begin
5 for i in 1 .. 10000
6 loop
7 for r in (select * from mytable where id in (1,2,3,4))
8 loop
9 v_id := r.id;
10 v_filler := r.filler;
11 end loop;
12 end loop;
13 end;
14 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.41
SQL> /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.39
SQL> /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.39
SQL> set timing off
ここでは、データベースに4倍以上のステートメントを送信するオーバーヘッドが最も重要であることがわかります。インデックスの深さとクラスタリング係数に応じて、あちこちでいくつかのバリエーションが可能ですが、1つのクエリと4つのクエリの違いが最大になります。
したがって、うまくいけば、このスレッドの読者は「IN句は高価であることが知られている」とはもう言わないでしょう:-)
よろしく、
ロブ。