10

同じ出力を生成する次の 2 つのクエリの実行時間が大幅に異なることに混乱しています。クエリは、約 450 万行のテーブルで Sqlite 3.7.9 で実行され、それぞれが ~50 行の結果を生成します。

クエリは次のとおりです。

% echo "SELECT DISTINCT acolumn FROM atable ORDER BY acolumn;" | time sqlite3 mydb
sqlite3 mydb  8.87s user 15.06s system 99% cpu 23.980 total


% echo "SELECT acolumn FROM (SELECT DISTINCT acolumn FROM atable) ORDER BY acolumn;" | time sqlite3 options
sqlite3 mydb  1.15s user 0.10s system 98% cpu 1.267 total

2 つのクエリのパフォーマンスを近づけるべきではありませんか? クエリプランナーが「ソート」操作と「個別」操作を異なる順序で実行している可能性があることは理解していますが、そうである場合、そうする必要がありますか? それとも、それを最速で行う方法を見つけられるべきですか?

編集:ここで要求されているのは、各クエリの「EXPLAIN QUERY PLAN」コマンドの出力です。

最初の (モノリシック) クエリの場合:

0|0|0|SCAN TABLE atable (~1000000 rows)
0|0|0|USE TEMP B-TREE FOR DISTINCT

2 番目の (サブクエリ) クエリの場合:

1|0|0|SCAN TABLE atable (~1000000 rows)
1|0|0|USE TEMP B-TREE FOR DISTINCT
0|0|0|SCAN SUBQUERY 1 (~1000000 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY
4

3 に答える 3

4

最初のクエリは、最初にすべてのレコードを並べ替えられた一時テーブルに挿入することによってレコードを並べ替え、次にそれらを調べて前のレコードDISTINCTと同一でないレコードのみを返すことによって を実装します。EXPLAIN(これは、以下に示す出力で確認できます。DISTINCT実際にGROUP BYは、同じように動作する に変換されます。)

2 番目のクエリは、理論的には最初のクエリと同じですが、SQLite のクエリ オプティマイザはかなり単純であり、この変換が安全であることを証明できません (サブクエリの平坦化のドキュメントで説明されているように)。したがって、DISTINCT最初の手順を実行し、重複していないもののみを一時テーブルに挿入してからORDER BY、2 番目の一時テーブルを使用して実装します。最初の一時テーブルは既にソートされているため、この 2 番目のステップは完全に不要ですが、どちらの一時テーブルにも格納されない重複が非常に多いため、これはとにかくデータにとってより高速です。

DISTINCT理論的には、最初のクエリは高速になる可能性があります。これは、SQLite がand句を同じ並べ替えられた一時テーブルORDER BYで実装できることを既に認識しているためです。ただし、実際には、重複を一時テーブルに格納する必要がないことを意味することを覚えておくほど、SQLite は賢くありません。(この特定の最適化は、メーリング リストでうまく質問すれば、SQLite に追加される可能性があります。)DISTINCT


$ sqlite3 mydb 
sqlite> .explain
sqlite> explain SELECT DISTINCT acolumn FROM atable ORDER BY acolumn;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Trace          0     0     0                    00               
1     SorterOpen     1     2     0     keyinfo(1,BINARY)  00               
2     Integer        0     3     0                    00  clear abort flag
3     Integer        0     2     0                    00  indicate accumulator empty
4     Null           0     6     6                    00               
5     Gosub          5     37    0                    00               
6     Goto           0     40    0                    00               
7     OpenRead       0     2     0     1              00  atable       
8     Rewind         0     14    0                    00               
9     Column         0     0     8                    00  atable.acolumn
10    Sequence       1     9     0                    00               
11    MakeRecord     8     2     10                   00               
12    SorterInsert   1     10    0                    00               
13    Next           0     9     0                    01               
14    Close          0     0     0                    00               
15    OpenPseudo     2     10    2                    00               
16    SorterSort     1     39    0                    00  GROUP BY sort
17    SorterData     1     10    0                    00               
18    Column         2     0     7                    20               
19    Compare        6     7     1     keyinfo(1,BINARY)  00               
20    Jump           21    25    21                   00               
21    Move           7     6     0                    00               
22    Gosub          4     32    0                    00  output one row
23    IfPos          3     39    0                    00  check abort flag
24    Gosub          5     37    0                    00  reset accumulator
25    Column         2     0     1                    00               
26    Integer        1     2     0                    00  indicate data in accumulator
27    SorterNext     1     17    0                    00               
28    Gosub          4     32    0                    00  output final row
29    Goto           0     39    0                    00               
30    Integer        1     3     0                    00  set abort flag
31    Return         4     0     0                    00               
32    IfPos          2     34    0                    00  Groupby result generator entry point
33    Return         4     0     0                    00               
34    Copy           1     11    0                    00               
35    ResultRow      11    1     0                    00               
36    Return         4     0     0                    00  end groupby result generator
37    Null           0     1     0                    00               
38    Return         5     0     0                    00               
39    Halt           0     0     0                    00               
40    Transaction    0     0     0                    00               
41    VerifyCookie   0     2     0                    00               
42    TableLock      0     2     0     atable         00               
43    Goto           0     7     0                    00               
sqlite> explain SELECT acolumn FROM (SELECT DISTINCT acolumn FROM atable) ORDER BY acolumn;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Trace          0     0     0                    00               
1     Goto           0     39    0                    00               
2     Goto           0     17    0                    00               
3     OpenPseudo     0     3     1                    01  coroutine for sqlite_subquery_DA7480_
4     Integer        0     2     0                    01               
5     OpenEphemeral  2     0     0     keyinfo(1,BINARY)  08               
6     OpenRead       1     2     0     1              00  atable       
7     Rewind         1     14    0                    00               
8     Column         1     0     3                    00  atable.acolumn
9     Found          2     13    3     1              00               
10    MakeRecord     3     1     4                    00               
11    IdxInsert      2     4     0                    00               
12    Yield          1     0     0                    00               
13    Next           1     8     0                    01               
14    Close          1     0     0                    00               
15    Integer        1     2     0                    00               
16    Yield          1     0     0                    00  end sqlite_subquery_DA7480_
17    SorterOpen     3     3     0     keyinfo(1,BINARY)  00               
18    Integer        2     1     0                    00               
19    Yield          1     0     0                    00  next row of co-routine sqlite_subquery_DA7480_
20    If             2     29    0                    00               
21    Column         0     0     5                    00  sqlite_subquery_DA7480_.acolumn
22    MakeRecord     5     1     6                    00               
23    Column         0     0     7                    00  sqlite_subquery_DA7480_.acolumn
24    Sequence       3     8     0                    00               
25    Move           6     9     0                    00               
26    MakeRecord     7     3     10                   00               
27    SorterInsert   3     10    0                    00               
28    Goto           0     19    0                    00               
29    OpenPseudo     4     6     1                    00               
30    OpenPseudo     5     11    3                    00               
31    SorterSort     3     37    0                    00               
32    SorterData     3     11    0                    00               
33    Column         5     2     6                    20               
34    Column         4     0     5                    20               
35    ResultRow      5     1     0                    00               
36    SorterNext     3     32    0                    00               
37    Close          4     0     0                    00               
38    Halt           0     0     0                    00               
39    Transaction    0     0     0                    00               
40    VerifyCookie   0     2     0                    00               
41    TableLock      0     2     0     atable         00               
42    Goto           0     2     0                    00               
于 2013-01-14T21:01:15.627 に答える
1

ほとんどの DBMS 内では、SQL ステートメントはリレーショナル代数に変換され、式ツリーで構造化されます。その後、dbms はヒューリスティックを使用してクエリを最適化します。主なヒューリスティックの 1 つは、「選択を早期に実行する」 (p.46) です。sqlite クエリ プランナーもこれを行うと思われるため、実行時間に違いがあります。

サブクエリの結果ははるかに小さいため (450 万に対して最大 50 行)、式ツリーの最後での並べ替えははるかに高速に行われます。(プレーン) 選択は非常に高価なプロセスではありません。多数の結果に対して操作を実行することは確かにコストがかかります。

于 2013-01-14T10:43:28.513 に答える
0

これは、サブセレクトで区切られた場合、注文操作と個別操作をより効率的に実装する必要があるためだと思います。

この実験は完了していません。以下も実行してください。

% echo "SELECT acolumn FROM (SELECT DISTINCT acolumn FROM atable ORDER BY acolumn) ;" | time sqlite3 mydb

これが平均して他の 2 つよりも時間がかかるかどうか教えてください。

于 2013-01-14T17:52:30.967 に答える