7

最高のパフォーマンスを発揮するようにインデックスを作成して、SQLクエリを最適化しようとしています。

テーブル定義

CREATE TABLE Mots (
  numero            INTEGER NOT NULL, 
  fk_dictionnaires integer(5) NOT NULL, 
  mot              varchar(50) NOT NULL, 
  ponderation      integer(20) NOT NULL,
  drapeau varchar(1) NOT NULL,
  CONSTRAINT pk_mots PRIMARY KEY(numero),
  CONSTRAINT uk_dico_mot_mots UNIQUE(fk_dictionnaires, mot),
  CONSTRAINT fk_mots_dictionnaires FOREIGN KEY(fk_dictionnaires) REFERENCES Dictionnaires(numero)
  );

インデックスの定義

CREATE INDEX idx_dictionnaires ON mots(fk_dictionnaires DESC);
CREATE INDEX idx_mots_ponderation ON mots(ponderation);
CREATE UNIQUE INDEX idx_mots_unique ON mots(fk_dictionnaires, mot);

SQLクエリ:

SELECT numero, mot, ponderation, drapeau 
FROM mots 
WHERE mot LIKE 'ar%' 
   AND fk_dictionnaires=1 
   AND LENGTH(mot)>=4 
   ORDER BY ponderation DESC 
LIMIT 5;

クエリプラン

0|0|0|SEARCH TABLE mots USING INDEX idx_dictionnaires (fk_dictionnaires=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY

定義されたインデックスは使用されていないようで、クエリは持続します(.timerによる):

CPU Time: user 0.078001 sys 0.015600

ただし、fk_dictionnaires=1を削除したとき。私のインデックスは正しく使用されており、パフォーマンスは約0.000000-0.01XXXXXX秒です

0|0|0|SCAN TABLE mots USING INDEX idx_mots_ponderation (~250000 rows)

私はstackoverflowでいくつかの同様の質問を見つけましたが、答えは私を助けません。

インデックスを使用したり、SQLクエリを変更したりして、パフォーマンスを向上させるにはどうすればよいですか?前もって感謝します。

4

1 に答える 1

5

SQLiteは、インデックスが非常にまばらであると考えているようで、を使用してスキャンする場合は、数行を調べるだけでidx_dictionnairesよいと結論付けています。idx_dictionnairesただし、引用したパフォーマンス結果は、2行以上を調べている必要があることを示しています。まず、試してみませんか?ANALYZE motsSQLiteは、利用可能な各インデックスのカーディナリティに関する最新情報を入手できますか?

SQLiteのドキュメントから、役立つ可能性のある他の何かがあります:


WHERE句の用語は、列名の前に単項+演算子を付けることにより、インデックスで使用するために手動で失格にすることができます。単項+はノーオペレーションであり、用語で指定されたテストの評価を遅くすることはありません。ただし、用語がインデックスを制約するのを防ぎます。したがって、上記の例で、クエリが次のように書き直された場合:

SELECT z FROM ex2 WHERE +x=5 AND y=6;

x列の+演算子は、その用語がインデックスを制約するのを防ぎます。これにより、ex2i2インデックスの使用が強制されます。

単項+演算子は、式から型アフィニティも削除することに注意してください。場合によっては、これにより、式の意味が微妙に変化する可能性があります。上記の例では、列xにTEXTアフィニティがある場合、比較「x=5」はテキストとして実行されます。ただし、+演算子はアフィニティを削除します。したがって、比較「+ x = 5」は、列xのテキストを数値5と比較し、常にfalseになります。


SQLiteが使用するのに最適なインデックスを選択するのに十分でない場合ANALYZE motsは、この機能を使用して、必要なインデックスを使用するように強制できます。

複合インデックスを試すこともできます-すでに定義したように見えますfk_dictionnaires,motが、SQLiteはそれを使用していません。ponderation「高速」クエリの場合、SQLiteは、クエリの最後で行を並べ替えないように、のインデックスを使用することを好むようです。にインデックスを追加しfk_dictionnaires,ponderation DESC、SQLiteが実際にそれを使用する場合、fk_dictionnaires=1テーブルスキャンなしで一致する行を選択し、最後の並べ替え回避できます。


POSTSCRIPT:上記で提案した複合インデックスは、OPのパフォーマンスの問題を「修正」しましたが、彼はまた、それがどのように、そしてなぜ機能するのかを尋ねました。@AGeiser、簡単な図を使用して、DBインデックスを直感的に理解できるようにします。

町で名前が「A」で始まるすべての人を見つける必要があると想像してください。すべての名前のディレクトリがありますが、それらはランダムな順序になっています。職業はなんですか?ディレクトリ全体を読んで、「A」で始まるものを選ぶしかない。大変な作業のようですね。(これは、インデックスのないDBテーブルのようなものです。)

しかし、誰かがあなたにすべての名前をアルファベット順にした電話帳をくれたらどうなるでしょうか?これで、「A」で始まる最初と最後のエントリを(バイナリ検索などを使用して)検索し、その範囲内のすべてのエントリを取得できます。本の中の他のすべての名前を見る必要さえありません。これははるかに高速になります。(これは、インデックスを持つDBテーブルのようなものです。この場合は、インデックスと呼びますlast_name,first_name。)

名前が「A」で始まるすべての人が必要で、2人が同じ名前の場合、郵便番号で並べ替える場合はどうでしょうか。「電話帳」(つまり、のインデックス)を使用して必要な名前をすばやく取得した場合でも、last_name,first_nameすべてを手動で並べ替える必要があります...そのため、再び多くの作業のように聞こえ始めます。この仕事を本当に簡単にするものは何ですか?

別の「電話帳」が必要になりますが、エントリが最初に名前で並べ替えられ、次に郵便番号で並べ替えられます。このような「電話帳」を使用すると、必要なエントリの範囲をすばやく選択でき、並べ替える必要もありません。すでに目的の順序になっています。(これはのインデックスlast_name,first_name,postal_codeです。)

この図は、検査する必要のある行の数を減らすだけでなく、必要な行が見つかった後の個別の「ソート」フェーズの必要性を(潜在的に)排除することによって、インデックスがSELECTクエリにどのように役立つかを明確にする必要があると思います。a,bうまくいけば、上の複合インデックスが上の複合インデックスとは完全に異なることも明らかになりb,aます。さらに「電話帳」の例をあげることもできますが、この回答は非常に長くなり、ブログ投稿のようになります。どのインデックスがクエリに役立つ可能性があるかについての直感を構築するために、「SQLアンチパターン」に関するO'Reillyの本(特に第13章「インデックスショットガン」)をお勧めします。

于 2012-08-16T09:44:33.343 に答える