[SQLite] SQLiteで実行計画を見てみよう その3

2017年8月7日月曜日

SQL sqlite

explain

[SQLite] SQLiteで実行計画を見てみよう その2ではテーブル結合の実行計画を取得して、nested loopの様子を見たけど、SQLiteではさらに詳細に内部の実行手順を見ることができる。

今度はexplain query planではなく、explainを使う。

The SQLite Bytecode Engine

[SQLite] SQLiteで実行計画を見てみよう その2で実行したSQLにexplainをつけて、見てみよう。
sqlite> explain select Test1.id, Test2.id, Test1.name from Test1
   ...> join Test2 on Test1.last_modified = Test2.last_modified;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     24    0                    00  NULL         
1     OpenRead       0     2     0     3              00  NULL         
2     OpenRead       1     86    0     3              00  NULL         
3     Rewind         0     22    0                    00  NULL         
4       Once           0     12    0                    00  NULL  
5       OpenAutoindex  2     2     0     k(2,B,)        00  NULL         
6       Rewind         1     12    0                    00  NULL         
7         Column         1     2     2                    00  NULL         
8         Rowid          1     3     0                    00  NULL         
9         MakeRecord     2     2     1                    00  NULL         
10        IdxInsert      2     1     0                    10  NULL         
11      Next           1     7     0                    03  NULL         
12      Column         0     2     4                    00  NULL         
13      IsNull         4     21    0                    00  NULL         
14      SeekGE         2     21    4     1              00  NULL         
15        IdxGT          2     21    4     1              00  NULL         
16        Rowid          0     5     0                    00  NULL         
17        IdxRowid       2     6     0                    00  NULL         
18        Column         0     1     7                    00  NULL         
19        ResultRow      5     3     0                    00  NULL         
20      Next           2     15    0                    00  NULL         
21    Next           0     4     0                    01  NULL         
22    Close          0     0     0                    00  NULL         
23    Halt           0     0     0                    00  NULL         
24    Transaction    0     0     12    0              01  NULL         
25    TableLock      0     2     0     Test1          00  NULL         
26    TableLock      0     86    0     Test2          00  NULL    
27    Goto           0     1     0                    00  NULL 

addr 25, 1, 3 を見ると、Test1が外側のループになっていて、addr 3-21がループ範囲になっていることがわかる。
addr 26, 2, 1 を見ると、Test2が内側のループになっていて、addr 6-11がループ範囲になっていることがわかる。
ただし、addr 4にOnceとあるようにaddr 5-11は初回のみ実行されて、ここでAutomatic Indexが構築される。
このため、実際の毎回実行される内部ループはaddr12-20となる。

検索条件ありの場合

今度は where Test2.name = 'HBnod4xm' をつけたバージョンを実行してみよう。
sqlite> explain select Test1.id, Test2.id, Test1.name from Test1
   ...> join Test2 on Test1.last_modified = Test2.last_modified
   ...> where Test2.name = 'HBnod4xm';
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     22    0                    00  NULL         
1     OpenRead       1     86    0     3              00  NULL         
2     OpenRead       2     115   0     k(2,,)         02  NULL         
3     OpenRead       0     2     0     3              00  NULL         
4     String8        0     1     0     HBnod4xm       00  NULL         
5     SeekGE         2     18    1     1              00  NULL         
6       IdxGT          2     18    1     1              00  NULL         
7       Seek           2     0     1                    00  NULL         
8       Rewind         0     17    0                    00  NULL         
9         Column         0     2     2                    00  NULL         
10        Column         1     2     3                    00  NULL         
11        Ne             3     16    2     (BINARY)       51  NULL         
12        Rowid          0     4     0                    00  NULL         
13        IdxRowid       2     5     0                    00  NULL         
14        Column         0     1     6                    00  NULL         
15        ResultRow      4     3     0                    00  NULL         
16      Next           0     9     0                    01  NULL         
17    Next           2     6     1                    00  NULL         
18    Close          1     0     0                    00  NULL         
19    Close          2     0     0                    00  NULL         
20    Close          0     0     0                    00  NULL         
21    Halt           0     0     0                    00  NULL         
22    Transaction    0     0     12    0              01  NULL         
23    TableLock      0     86    0     Test2          00  NULL         
24    TableLock      0     2     0     Test1          00  NULL         
25    Goto           0     1     0                    00  NULL   

addr 2でオープンした範囲を5-17でループしている。
明示的には書かれていないけど、addr 2のP2の115がTest2.nameのインデックスを指しているようだ。
内側のループはaddr 8-16になる。
こちらはRewindでテーブルをscanしているのがわかる。

こうして見れば、nested loopがまさにnested loopだというのがよくわかるね。