1

主キーとして6つの数字で構成されるテーブルがあります

CREATE TABLE table1 ( num1 decimal, num2 int, num3 int, num4 bigint, num5 bigint, num6 bigint,
PRIMARY KEY (num1, num2, num3, num4, num5, num6))

並べ替えられた順序でテーブルにアクセスする必要があり、多くの場合、テーブルをクエリして、次の N 個の大きな数字とそれに関連するデータを見つける必要があります。

だから私が書いたクエリはこのようなものでした

SELECT * FROM table1 WHERE  
 num1 >? OR (  
 (num1 == ? AND num2 > ?) OR (  
 (num1 == ? AND num2 == ? AND num3 > ?) OR (  
 (num1 == ? AND num2 == ? AND num3 == ? AND num4 > ? OR (  
 (num1 == ? AND num2 == ? AND num3 == ? AND num4 == ? AND num5 > ?) OR (  
 (num1 == ? AND num2 == ? AND num3 == ?  
 AND num4 == ? AND num5 == ? AND num6 > ?)))))) ORDER BY num1, num2, num3, num4, num5, num6  
 LIMIT ?;

これは、次の最大のキーを見つけるために私が見ることができる最良の方法であり、これはインデックスの順序でクエリを実行します....クエリには数秒かかるため、私は好きではありません.

パフォーマンスを向上させる方法はありますか?これは、1,000 万行のテーブルで実行するのに数秒かかり、100 ミリ秒のオーダーでさらに実行する必要があります。

クエリ プラン:

"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1>?) (~250000 rows)"
"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1=? AND num2>?) (~2 rows)"
"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1=? AND num2=? AND num3>?) (~2 rows)"
"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1=? AND num2=? AND num3=? AND num4>?) (~2 rows)"
"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1=? AND num2=? AND num3=? AND num4=? AND num5>?) (~1 rows)"
"SEARCH TABLE table1 USING INDEX sqlite_autoindex_table1_1 (num1=? AND num2=? AND num3=? AND num4=? AND num5=? AND num6>?) (~1 rows)"
"USE TEMP B-TREE FOR ORDER BY"

編集:

なぜこれが不可能なのですか?ORDER BY私は文字通り、キーワードによって生成されたのと同じ順序である INDEXED ORDER で物事を取得したいですか?

4

3 に答える 3

1

他のより洗練された RDBMS とは対照的に、sqlite にはルールベースのクエリ オプティマイザがあります。つまり、実行計画はクエリの記述方法 (および句の順序) に大きく依存します。これにより、オプティマイザーは非常に予測可能になり、sqlite が実行計画を生成する方法を知っていれば、この予測可能性を利用して問題を解決できます。

最初のアイデアは、(num1>?) や (num1=? and num2>?) のようなさまざまな句が互いに素な結果を生成しており、これらの結果が互いに自然にソートされることに注意することです。クエリがサブクエリ (それぞれが条件の一部を処理する) に分割され、並べ替えられた結果が生成される場合、サブクエリが正しい順序で実行されていれば、すべての結果セットの連結も並べ替えられます。

たとえば、次のクエリを考えてみましょう。

select * from table1 where num1=? and num2>? order by num1,num2
select * from table1 where num1>? order by num1,num2

これらのクエリによって生成される 2 つの結果セットは互いに素であり、最初の結果セットの行は常に 2 番目の結果セットの行の前に並べられます。

2 番目のアイデアは、sqlite が LIMIT 句を処理する方法を理解することです。実際には、クエリの先頭でカウンターを宣言し、選択された行ごとにこのカウンターをデクリメントしてテストするため、クエリを早期に停止できます。

たとえば、次のクエリを考えてみましょう。

.explain
explain select * from (
   select * from table1 where num1=? and num2>?
   union all
   select * from table1 where num1>?
) limit 10;

sqlite は、クエリで指定された順序でサブクエリを評価します。最初のサブクエリが 10 を超える行を返す場合、2 番目のサブクエリは実行されません。プランを表示することで簡単に確認できます。

addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Trace          0     0     0                    00               
1     Integer        10    1     0                    00               
2     Variable       1     2     2                    00               
3     Goto           0     44    0                    00               
4     OpenRead       3     3     0     keyinfo(6,BINARY,BINARY)  00               
5     SCopy          2     4     0                    00               
6     IsNull         4     23    0                    00               
7     SCopy          3     5     0                    00               
8     IsNull         5     23    0                    00               
9     Affinity       4     2     0     cd             00               
10    SeekGt         3     23    4     2              00               
11    IdxGE          3     23    4     1              01               
12    Column         3     1     6                    00               
13    IsNull         6     22    0                    00               
14    Column         3     0     7                    00               
15    Column         3     1     8                    00               
16    Column         3     2     9                    00               
17    Column         3     3     10                   00               
18    Column         3     4     11                   00               
19    Column         3     5     12                   00               
20    ResultRow      7     6     0                    00               
21    IfZero         1     23    -1                   00               
22    Next           3     11    0                    00               
23    Close          3     0     0                    00               
24    IfZero         1     43    0                    00               
25    Variable       3     13    1                    00               
26    OpenRead       4     3     0     keyinfo(6,BINARY,BINARY)  00               
27    SCopy          13    14    0                    00               
28    IsNull         14    42    0                    00               
29    Affinity       14    1     0     c              00               
30    SeekGt         4     42    14    1              00               
31    Column         4     0     6                    00               
32    IsNull         6     41    0                    00               
33    Column         4     0     7                    00               
34    Column         4     1     8                    00               
35    Column         4     2     9                    00               
36    Column         4     3     10                   00               
37    Column         4     4     11                   00               
38    Column         4     5     12                   00               
39    ResultRow      7     6     0                    00               
40    IfZero         1     42    -1                   00               
41    Next           4     31    0                    00               
42    Close          4     0     0                    00               
43    Halt           0     0     0                    00               
44    Transaction    0     0     0                    00               
45    VerifyCookie   0     3     0                    00               
46    TableLock      0     2     0     table1         00               
47    Goto           0     4     0                    00               

カウンタはステップ 1 で宣言され、ステップ 21、24、40 でデクリメント/テストされます。

これら 2 つの注意事項を組み合わせることで、きれいではありませんが、効率的な実行計画を生成するクエリを提案できます。

SELECT * FROM (
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 == ? AND num2 == ? AND num3 == ? AND num4 == ? AND num5 == ? AND num6 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
  UNION ALL
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 == ? AND num2 == ? AND num3 == ? AND num4 == ? AND num5 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
  UNION ALL
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 == ? AND num2 == ? AND num3 == ? AND num4 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
  UNION ALL
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 == ? AND num2 == ? AND num3 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
  UNION ALL
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 == ? AND num2 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
  UNION ALL
  SELECT * FROM ( SELECT * FROM table1
                  WHERE num1 > ?
                  ORDER BY num1, num2, num3, num4, num5, num6 )
) LIMIT ?;

外側のクエリでは「order by」句は必要ないため、sqlite がすべてのサブクエリを実行する必要がないことに注意してください。したがって、正しい行数になったら停止できます。サブクエリの順序は重要です。

「ユニオンオール」の前に「オーダーバイ」を使用できないため、第 2 レベルの内部サブクエリが必要です。それらは sqlite によって最適化されているため、問題にはなりません。

777K 行を含むダミー テーブルでは、最初のクエリのコストは次のとおりです。

strace -c -eread,lseek sqlite3 toto.db < q1.sql
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 63.57    0.001586           0     18556           read
 36.43    0.000909           0     18544           lseek
------ ----------- ----------- --------- --------- ----------------
100.00    0.002495                 37100           total

私の費用は次のとおりです。

strace -c -eread,lseek sqlite3 toto.db < q3.sql
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  -nan    0.000000           0        18           read
  -nan    0.000000           0         8           lseek
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    26           total
于 2012-08-14T18:12:10.577 に答える
1

そのような場合はクエリオプティマイザーで対処する必要があると思いますが、aqliteでは非常に単純なので、@cyroxxが書いたようにテーブル構造を変更する方が本当に良いでしょう.

その他の考え: 他の方法でクエリを書き直すこともできます。オプティマイザーは必要なものを理解するかもしれません。たとえば、次のことを試すことができます。

SELECT * FROM table1 WHERE
    num1 > 1   OR
  ( num1 = 1   AND ( num2 > 2 OR
                   ( num2 = 2 AND ( num3 > 3 OR
                                  ( num3 = 3 AND ( num4 > 4 OR
                                                 ( num4 = 4 AND ( num5 > 5 OR
                                                                ( num5 = 5 AND num6 > 6)
                                                                )
                                                 )
                                                 )
                                  )
                                  )
                   )
                   )
  )

それは良くなるかもしれません(または悪くなります:))。

于 2012-08-13T14:36:04.443 に答える
1

これが問題の有効な解決策である場合: 6 つの部分からなる自然キーの代わりに単一の代理キーを使用することを検討する必要があります。

あなた自身の例からわかるように、主キーに基づいてルックアップを行うだけでは非常に複雑です。1 つの列だけを考慮するのではなく、複数のインデックス ルックアップを実行する必要があります。各インデックス ルックアップには、ディスク レイテンシが含まれます。これは、この場合、全体の処理時間を容易に支配します。

投稿したクエリ プランを確認してください。最初のルックアップの後、返される行数はすでに 2 つに減り、残りのインデックスのクエリは、各ステップの後に除外できる行数 (0-1 行) と比較してコストがかかります。

したがって、主キーとして整数型の単一の列のみをクエリする必要がある場合は、 SQLite docsからわかるように、パフォーマンスが大幅に向上するはずです。

SQLite の各テーブルのデータは、rowid 値をキーとして使用して、各テーブル行のエントリを含む B ツリー構造として格納されます。これは、ROWID によるレコードの取得またはソートが高速であることを意味します。特定の行 ID を持つレコード、または指定された範囲内の行 ID を持つすべてのレコードの検索は、他の PRIMARY KEY またはインデックス付きの値を指定して行われる同様の検索よりも約 2 倍高速です。

1 つの例外を除いて、単一の列で構成される主キーがテーブルにあり、その列の宣言された型が大文字と小文字の任意の組み合わせである "INTEGER" である場合、その列は行 ID のエイリアスになります。このような列は通常、「整数主キー」と呼ばれます。

さらに、SQL クエリがよりシンプルになり、より簡単に維持できるようになります。

于 2012-08-10T23:10:51.213 に答える