Session by Shinnosuke Akita
2016.03.30
SQL チューニング勉強会
Self Introduction
秋田 進之助 (Shinnosuke Akita)
株式会社バルテック所属  4 年目
 ‐データベースの構築・トラブルシューティングな
ど
著書著書
 「グラフ型データベース入門 」 2016 年
セッションスピーカーセッションスピーカー
 「 Oracle Database のバックアップを考えよう」
             2015 年 @JPOUG
 「 Smart Scan 奮闘記」 2014 年 @JPOUG など
所属団体所属団体
  JPOUG ( Japan Oracle User Group)
  Neo4j ユーザ会
Today’s Agenda
・ SQL の流れ
・実行計画について
・なぜ索引は速いのか
・索引の種類とオペレーション
・結合の種類と特徴
・ヒント句
・実行計画の確認方法
・デモ
SQLの流れ
SQLは、どうやってDBサーバに認識され、
実行されるのだろうか?
SQLが流れるまで
オープン 解析 バインド 実行 フェッチ クローズ
実行フェー
ズ
主な動き
解析 文の構文チェック、権限チェック、実行計画作成
バインド バインド変数のバインド
フェッチ 対象レコードをローカル変数に取り出す
実行計画とは?
解析のフェーズに含まれる。
テーブルをフルスキャン(すべて読み込む)するか、インデックスを使
用するか。どのような結合を使用するかなどを解析し、計画を立てる。
オープン 解析 バインド 実行 フェッチ クローズ
ソフトパースとハードパース
オープン 解析 バインド 実行 フェッチ クローズ
SQL の実行フェーズに「解析」があるが、解析には時間がかかる。
一度作成された実行計画は Oracle の共有プールに格納され、データが入
りきらなくなって追い出される(エージアウト)まで保持され、作成さ
れた計画は再利用される。
共有プールを節約するために、 WHERE 句の値をバインド変数化するこ
とも有効である。
     ( 例 ) WHERE   SYAINNO = :a;
実行計画を見てみよう
実行計画
----------------------------------------------------------
Plan hash value: 2520579295  
------------------------------------------------------------------------------
| Id  | Operation        | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |           |     1 |     4 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| TEST2_IX1 |     1 |     4 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
 
  1 - access("NUM"=1)    
実行する処理が表示される。
インデックスのスキャンが行われ
る
処理に使用されるオブジェクトが
表示される。
索引はなぜ速いのか
1-6 7-12
1
索引は、以下のような構造をしています。インデックスの「リーフ」の場所
には実テーブルの行番号が格納されており、テーブルを一から探すよりも速く
なります。
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12 リーフ
Col1 Col2
2 5
8 20
1 12
7 8
9 1
3 2
4 9
10 6
5 4
6 3
col1=2 は
1 行目
なぜ索引は速いのか?
1-6 7-12
1
しかしながら、インデックスが検索条件と合わない場合は、インデックスを経
由する分だけテーブルフルスキャンよりも効率が悪くなってしまいます。
col1 のインデックスがあるが、 col2 を条件とした場合、強制的にインデックス
読み込みにしようとすると、すべてのインデックスを確認する必要があります
。
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12 リーフ
SELECT col1,col2 FROM test2
WHERE col2=1;
SELECT col1,col2 FROM test2
WHERE col2=1;Col1 のインデックス
なぜ索引は速いのか?
1-6 7-12
1
テーブルの挿入をする際に、インデックスは並び替えがされます。
インデックスもデータブロックごとに管理しており、新たに行挿入が発生した
際にインデックス用のブロックを追加作成したり、ブロックを分割するなどの
処理が発生し、インデックスなしの挿入よりパフォーマンスが落ちます。
インデックスはたくさん貼っておけばよいわけではありません。
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12 リーフ
Col1 のインデックス
13
13
13
オペレーションの種類(主なも
の)
Operation 処理内容
TABLE ACCESS FULL 表のすべての行を取得する
INDEX RANGE SCAN 索引から範囲をキーにして取得する
INDEX UNIQUE SCAN 索引ブロックをピンポイントで取得する
INDEX FAST FULL SCAN 索引ブロックをすべてスキャンする
NESTED LOOP ネステッドループ結合を使用した結合処理
MERGE JOIN ソートマージ結合を使用した結合処理
HASH JOIN ハッシュによる 2 つの行セットの結合
索引の種類
1-6 7-12
1
B-Tree 型
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12 リーフ
木構造になっており、データを効率よく検索できる。
索引の種類
Bitmap 型
鴻巣マラソン
リレーマラソン
皇居練習会1
皇居練習会2
1 1 1 1 1 1 1
1 1 1 1 1 1 0
1 1 0 1 0 1 1
1 1 0 0 1 1 1
  H 部長    S 社長    A 部員     I 部員    M 部員    H 部員    Y
部員
※ 1:参加 0:不参加
値が2種類しかないなど、カージナリティ(値の種類)が低い場合は、
Bitmap 型の 1/0 での表記のほうがパフォーマンスが良いこともある。
索引のオペレーション
1-6 7-12
1
INDEX UNIQUE SCAN  : 索引ブロックをピンポイントで取得す
る 
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12
WHERE SYAINID = 6;
索引のオペレーション
1-6 7-12
1
INDEX RANGE SCAN  : 索引から範囲をキーにして取得する 
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12
WHERE SYAINID BETWEEN 4 AND 6;
索引のオペレーション
1-6 7-12
1
INDEX FAST FULL SCAN  : 索引ブロックをすべてスキャンする
 
1-3 4-6 7-9 10-12
2 3 4 5 6 7 8 9 10 11 12
WHERE SYAINID BETWEEN 1 AND 9;
結合の種類
NESTED LOOP  : ネステッドループ結合を使用した結合処理 
SYAINCD SYAINNAME JOB MGR KONOSU_FLG
1 H PROGRAMMER 2 1
2 S     PRESIDENT 1
3 A     DBA 1 1
4 H SALESMAN 2 1
5 I PROGRAMMER 1 0
EMP 表
 
JOB SAL COMM
PRESIDENT 8000 2000
SALESMAN 5000 3000
PROGRAMMER 4000 1000
DBA 3000 1000
SAL 表
 
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
結合の種類
NESTED LOOP  : ネステッドループ結合を使用した結合処理 
SYAINCD SYAINNAME JOB MGR KONOSU_FLG
1 H PROGRAMMER 2 1
2 S     PRESIDENT 1
3 A     DBA 1 1
4 H SALESMAN 2 1
5 I PROGRAMMER 1 0
EMP 表
 
JOB SAL COMM
PRESIDENT 8000 2000
SALESMAN 5000 3000
PROGRAMMER 4000 1000
DBA 3000 1000
SAL 表
 
1)
EMP 表を順に検索し、 JOB = PROGRAMMER
という値を取得する
2)
EMP 表を順に検索し、 JOB = PROGRAMMER
という値を取得する
結合の種類
MERGE JOIN  : ソートマージ結合を使用した結合処理 
SYAINC
D
SYAINNAME JOB MGR KONOSU_
FLG
1 H PROGRAMMER 2 1
2 S     PRESIDENT 1
3 A     DBA 1 1
4 H SALESMAN 2 1
5 I PROGRAMMER 1 0
EMP 表
 
JOB SAL COMM
PRESIDENT 8000 2000
SALESMAN 5000 3000
PROGRAMMER 4000 1000
DBA 3000 1000
SAL 表
 
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
結合の種類
MERGE JOIN  : ソートマージ結合を使用した結合処理
SYAINCD SYAINNAME JOB MGR KONOSU_FLG
3 A DBA 1 1
2 S PRESIDENT 1
1 H PROGRAMMER 2 1
5 I PROGRAMMER 1 0
4 H SALESMAN 2 1
EMP 表
 
JOB SAL COMM
DBA 3000 1000
PRESIDENT 8000 2000
PROGRAMMER 4000 1000
SALESMAN 5000 3000
SAL 表
 
1)
結合条件をソートする
2)
結合条件をソートする
SYAINNAME JOB SAL
A DBA 3000
S PRESIDENT 8000
H PROGRAMMER 4000
I PROGRAMMER 4000
H SALESMAN 5000
3)
それぞれマージする
結合の種類
HASH JOIN  : ハッシュによる 2 つの行セットの結合処理 
SYAINC
D
SYAINNAME JOB MGR KONOSU_
FLG
1 H PROGRAMMER 2 1
2 S     PRESIDENT 1
3 A     DBA 1 1
4 H SALESMAN 2 1
5 I PROGRAMMER 1 0
EMP 表
 
JOB SAL COMM
PRESIDENT 8000 2000
SALESMAN 5000 3000
PROGRAMMER 4000 1000
DBA 3000 1000
SAL 表
 
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
SELECT emp.syainname , sal.sal
FROM emp, sal
WHERE emp.job = sal.job;
結合の種類
HASH JOIN  : ハッシュによる 2 つの行セットの結合処理 
SYAINC
D
SYAINNAME JOB MGR KONOSU_
FLG
1 H PROGRAMMER 2 1
2 S     PRESIDENT 1
3 A     DBA 1 1
4 H SALESMAN 2 1
5 I PROGRAMMER 1 0
EMP 表
 
JOB SAL COMM
PRESIDENT 8000 2000
SALESMAN 5000 3000
PROGRAMMER 4000 1000
DBA 3000 1000
SAL 表
 
2 )
もう一つの表の結合条件式に
ハッシュ関数をかけて、
結合できるかをハッシュテーブルで確認
SYAINI
D
SAL
1 4000
2 8000
3 3000
4 5000
5 4000
HASH TABLE
 
1)
レコード数の少ない表の結合条件式を
ハッシュ関数にかけて、
メモリ上にハッシュテーブルを作成
ハッシュ関数
ハ
ッ
シ
ュ
関
数
ヒント句
オペレーション( SQL を実行する際の処理方法)を固定させることが
できる。
Operation ヒント句 使用例
TABLE ACCESS
FULL
FULL SELECT /*+ FULL(tab) */ * from
tab;
INDEX RANGE
SCAN
INDEX SELECT /*+ INDEX(tab idx) */ *
from tab;
INDEX UNIQUE
SCAN
INDEX FAST FULL
SCAN
NESTED LOOP USE_NL SELECT /*+ USE_NL(a b)*/ * from
a,b;
MERGE JOIN USE_MERG
E
SELECT /*+ USE_MERGE (a b)*/ *
from a,b;
実行計画の確認方法
では、実際に実行計画を確認します。
SQL> explain plan for
SQL> select * from tab;
・実行計画出力
  explain plan for の後に出力したい SQL を入力します。
SQL> @?/rdbms/admin/utlxpls.sql
・実行計画表示
実行計画の確認方法
ただし、現在実行されている SQL の実行計画を出力したい場合は、
必ずしも正しいとは限りません。
この場合は、共有プール内の実行計画情報を確認します。
SQL> SELECT * FROM table
(DBMS_XPLAN.DISPLAY_CURSOR(‘SQL_ID'));
DEMO
それではデモで実際に実行計画の変化を確認してみ
ましょう!

SQLチューニング勉強会資料