2012-05-10 9 views
31

私は今約1週間使用しているクエリのスピードアップに取り組んでおり、ここでいくつかの質問があります(How can I speed up fetching the results after running an sqlite query?Is it normal that sqlite.fetchall() is so slow?How to use min() and max() in an efficient way?)。なぜ2倍の結果しか得られない場合、同じSQLiteクエリが30倍遅くなるのですか?

そこの答えの非常に有用な助けを借りて、私は100.95秒とfetchall取るsqliteクエリに時間を得ることができました:1485.43。これではまだ十分ではなかったので、いくつかの異なるインデックスを試してみた結果、1サンプルで0.08秒、フェッチの時間が54.97秒に短縮されました。だから私は最終的に物事をスピードアップすることができたと思った。

次に、0.58秒をとるフェッチと3952.80秒をとって次のサンプルに対してクエリが実行されます。 3番目のサンプルでは、​​クエリには1.01秒かかり、fetchallには1970.67秒かかりました。

最初のサンプルは12951行を取り出し、2番目のサンプルは24972行と3番目の6470行を取り出しました。 2番目の例のようにフェッチするのに必要な量が約半分になったときに、最初のサンプルが非常に高速に行をフェッチする理由は非常に不思議です。


コード(spectrumFeature_inputValuesを用いる3つのサンプルから、(1)、(2)及び(3)である。):

self.cursor.execute('begin') 
self.cursor.execute("EXPLAIN QUERY PLAN "+ 
        "SELECT precursor_id, feature_table_id "+ 
        "FROM `MSMS_precursor` "+ 
        "INNER JOIN `spectrum` ON spectrum.spectrum_id = MSMS_precursor.spectrum_spectrum_id "+ 
        "INNER JOIN `feature` ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+ 
        "WHERE spectrum.scan_start_time BETWEEN feature.rtMin AND feature.rtMax "+ 
        "AND MSMS_precursor.ion_mz BETWEEN feature.mzMin AND feature.mzMax "+ 
        "AND feature.msrun_msrun_id = ?", spectrumFeature_InputValues) 
print 'EXPLAIN QUERY PLAN: ' 
print self.cursor.fetchall() 
import time 
time0 = time.time() 
self.cursor.execute("SELECT precursor_id, feature_table_id "+ 
        "FROM `MSMS_precursor` "+ 
        "INNER JOIN `spectrum` ON spectrum.spectrum_id = MSMS_precursor.spectrum_spectrum_id "+ 
        "INNER JOIN `feature` ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+ 
        "WHERE spectrum.scan_start_time BETWEEN feature.rtMin AND feature.rtMax "+ 
        "AND MSMS_precursor.ion_mz BETWEEN feature.mzMin AND feature.mzMax "+ 
        "AND feature.msrun_msrun_id = ?", spectrumFeature_InputValues) 
print 'query took:',time.time()-time0,'seconds' 
time0 = time.time() 
precursorFeatureIds = self.cursor.fetchall() 
print 'it fetched:',len(precursorFeatureIds),'rows' 
print 'fetchall took',time.time()-time0,'seconds' 
time0 = time.time() 
for precursorAndFeatureID in precursorFeatureIds: 
    feature_has_MSMS_precursor_inputValues = (precursorAndFeatureID[0], precursorAndFeatureID[1]) 
    self.cursor.execute("INSERT INTO `feature_has_MSMS_precursor` VALUES(?,?)", feature_has_MSMS_precursor_inputValues) 
print 'inserting took',time.time()-time0,'seconds' 
self.connection.commit() 

および結果:

EXPLAIN QUERY PLAN: 
[(0, 0, 2, u'SCAN TABLE feature (~100000 rows)'), (0, 1, 1, u'SEARCH TABLE spectrum USING INDEX fk_spectrum_scahn_start_time_1 (scan_start_time>? AND scan_start_time<?) (~3125 rows)'), (0, 2, 0, u'SEARCH TABLE MSMS_precursor USING INDEX fk_MSMS_precursor_spectrum_spectrum_id_1 (spectrum_spectrum_id=?) (~5 rows)')] 
query took: 0.0754859447479 seconds 
it fetched: 12951 rows 
fetchall took 54.2855291367 seconds 
inserting took 0.602859973907 seconds 
It took 54.9704811573 seconds 

EXPLAIN QUERY PLAN: 
[(0, 0, 2, u'SCAN TABLE feature (~100000 rows)'), (0, 1, 1, u'SEARCH TABLE spectrum USING INDEX fk_spectrum_scahn_start_time_1 (scan_start_time>? AND scan_start_time<?) (~3125 rows)'), (0, 2, 0, u'SEARCH TABLE MSMS_precursor USING INDEX fk_MSMS_precursor_spectrum_spectrum_id_1 (spectrum_spectrum_id=?) (~5 rows)')] 
query took: 0.579694032669 seconds 
it fetched: 24972 rows 
fetchall took 3950.08093309 seconds 
inserting took 2.11575508118 seconds 
It took 3952.80745602 seconds 

EXPLAIN QUERY PLAN: 
[(0, 0, 2, u'SCAN TABLE feature (~100000 rows)'), (0, 1, 1, u'SEARCH TABLE spectrum USING INDEX fk_spectrum_scahn_start_time_1 (scan_start_time>? AND scan_start_time<?) (~3125 rows)'), (0, 2, 0, u'SEARCH TABLE MSMS_precursor USING INDEX fk_MSMS_precursor_spectrum_spectrum_id_1 (spectrum_spectrum_id=?) (~5 rows)')] 
query took: 1.01185703278 seconds 
it fetched: 6470 rows 
fetchall took 1970.622962 seconds 
inserting took 0.673867940903 seconds 
It took 1972.31343699 seconds 

SQLiteは文を作成します。

-- ----------------------------------------------------- 
-- Table `feature` 
-- ----------------------------------------------------- 
CREATE TABLE IF NOT EXISTS `feature` (
    `feature_table_id` INT PRIMARY KEY NOT NULL , 
    `feature_id` VARCHAR(40) NOT NULL , 
    `intensity` DOUBLE NOT NULL , 
    `overallquality` DOUBLE NOT NULL , 
    `charge` INT NOT NULL , 
    `content` VARCHAR(45) NOT NULL , 
    `intensity_cutoff` DOUBLE NOT NULL, 
    `mzMin` DOUBLE NULL , 
    `mzMax` DOUBLE NULL , 
    `rtMin` DOUBLE NULL , 
    `rtMax` DOUBLE NULL , 
    `msrun_msrun_id` INT NOT NULL , 
    CONSTRAINT `fk_feature_msrun1` 
    FOREIGN KEY (`msrun_msrun_id`) 
    REFERENCES `msrun` (`msrun_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION); 

    CREATE INDEX `fk_mzMin_feature` ON `feature` (`mzMin` ASC); 
    CREATE INDEX `fk_mzMax_feature` ON `feature` (`mzMax` ASC); 
    CREATE INDEX `fk_rtMin_feature` ON `feature` (`rtMin` ASC); 
    CREATE INDEX `fk_rtMax_feature` ON `feature` (`rtMax` ASC); 

DROP TABLE IF EXISTS `spectrum`; 
-- ----------------------------------------------------- 
-- Table `spectrum` 
-- ----------------------------------------------------- 
CREATE TABLE IF NOT EXISTS `spectrum` (
    `spectrum_id` INT PRIMARY KEY NOT NULL , 
    `spectrum_index` INT NOT NULL , 
    `ms_level` INT NOT NULL , 
    `base_peak_mz` DOUBLE NOT NULL , 
    `base_peak_intensity` DOUBLE NOT NULL , 
    `total_ion_current` DOUBLE NOT NULL , 
    `lowest_observes_mz` DOUBLE NOT NULL , 
    `highest_observed_mz` DOUBLE NOT NULL , 
    `scan_start_time` DOUBLE NOT NULL , 
    `ion_injection_time` DOUBLE, 
    `binary_data_mz` BLOB NOT NULL, 
    `binary_data_rt` BLOB NOT NULL, 
    `msrun_msrun_id` INT NOT NULL , 
    CONSTRAINT `fk_spectrum_msrun1` 
    FOREIGN KEY (`msrun_msrun_id`) 
    REFERENCES `msrun` (`msrun_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION); 

CREATE INDEX `fk_spectrum_spectrum_id_1` ON `spectrum` (`spectrum_id` ASC); 
CREATE INDEX `fk_spectrum_scahn_start_time_1` ON `spectrum` (`scan_start_time` ASC); 

DROP TABLE IF EXISTS `feature_has_MSMS_precursor`; 
-- ----------------------------------------------------- 
-- Table `spectrum_has_feature` 
-- ----------------------------------------------------- 
CREATE TABLE IF NOT EXISTS `feature_has_MSMS_precursor` (
    `MSMS_precursor_precursor_id` INT NOT NULL , 
    `feature_feature_table_id` INT NOT NULL , 
    CONSTRAINT `fk_spectrum_has_feature_spectrum1` 
    FOREIGN KEY (`MSMS_precursor_precursor_id`) 
    REFERENCES `MSMS_precursor` (`precursor_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION, 
    CONSTRAINT `fk_spectrum_has_feature_feature1` 
    FOREIGN KEY (`feature_feature_table_id`) 
    REFERENCES `feature` (`feature_table_id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION); 

    CREATE INDEX `fk_feature_has_MSMS_precursor_feature1` ON `feature_has_MSMS_precursor` (`feature_feature_table_id` ASC); 
    CREATE INDEX `fk_feature_has_MSMS_precursor_precursor1` ON `feature_has_MSMS_precursor` (`MSMS_precursor_precursor_id` ASC); 

mzrtの値は、スペクトルと機能の両方でインデックスを作成しています。これらの数値を一緒に比較するのにほとんどの時間が費やされていると考えていました。

なぜ、最初のサンプルが2番目と3番目のサンプルよりもずっと速いのですか?そして、クエリ時間はfetchall時間とどう関係していますか?最も重要なのは、これをスピードアップできる方法はあるのでしょうか?


アップデート1:

2D寸法(rtMin、RTMAX、mzMin、mzMax)にポイントを比較すると、2時間がかかるのn ^ますので、それはおそらくだcollegaueに話をした後。このおよそは、60^2秒(1回目のフェッチが近づいた時間)を少し超えた2番目のフェッチに対応し、行の量の2倍より少し少ない値を取得しました。しかし、これは私の質問のいずれにも答えません。


アップデート2:

私はコメントで助言としてのR *ツリーを使用してみました。

CREATE VIRTUAL TABLE convexhull_edges USING rtree(
    feature_feature_table_id,    
    rtMin, rtMax,  
    mzMin, mzMax,  
); 

とする私のクエリを変更します:私は、新しいテーブルを作っ

self.cursor.execute("SELECT precursor_id, feature_table_id "+ 
        "FROM `MSMS_precursor` "+ 
        "INNER JOIN `spectrum` ON spectrum.spectrum_id = MSMS_precursor.spectrum_spectrum_id "+ 
        "INNER JOIN `feature` ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+ 
        "INNER JOIN `convexhull_edges` ON convexhull_edges.feature_feature_table_id = feature.feature_table_id " 
        "WHERE spectrum.scan_start_time BETWEEN convexhull_edges.rtMin AND convexhull_edges.rtMax "+ 
        "AND MSMS_precursor.ion_mz BETWEEN convexhull_edges.mzMin AND convexhull_edges.mzMax "+ 
        "AND feature.msrun_msrun_id = ?", spectrumFeature_InputValues) 

これは、次のような結果が得られた:私の以前の方法よりも

​​3210

ので、少し速くし、それでもそれほど速くない。次に、web_bodのソリューションを試してみましょう。 web_bodのソリューションを使用して


アップデート3

私は、次の回を得た:

EXPLAIN QUERY PLAN: 
[(0, 0, 2, u'SCAN TABLE feature (~100000 rows)'), (0, 1, 1, u'SEARCH TABLE spectrum USING INDEX fk_spectrum_scahn_start_time_1 (scan_start_time>? AND scan_start_time<?) (~3125 rows)'), (0, 2, 0, u'SEARCH TABLE MSMS_precursor USING INDEX fk_MSMS_precursor_spectrum_spectrum_id_1 (spectrum_spectrum_id=?) (~5 rows)')] 
query took: 0.0521960258484 seconds 
it fetched: 13052 rows 
fetchall took 90.5810132027 seconds 

EXPLAIN QUERY PLAN: 
[(0, 0, 2, u'SCAN TABLE feature (~100000 rows)'), (0, 1, 1, u'SEARCH TABLE spectrum USING INDEX fk_spectrum_scahn_start_time_1 (scan_start_time>? AND scan_start_time<?) (~3125 rows)'), (0, 2, 0, u'SEARCH TABLE MSMS_precursor USING INDEX fk_MSMS_precursor_spectrum_spectrum_id_1 (spectrum_spectrum_id=?) (~5 rows)')] 
query took: 0.278959989548 seconds 
it fetched: 25195 rows 
fetchall took 4310.6seconds 

三つ目は悲しげにあるため、再起動を終了しませんでした。だから、これは少しは速く私の最初のソリューションよりもですが、私はそれが中断されずに行っていたことを見、信じられないほど遅い起こっていた別のクエリで作業するR *ツリー


更新4

を使用するよりも遅く睡眠(this question参照)。そこで、このクエリを実行している最中にトップを確認し、RとDの状態を切り替えて、CPU使用率を100%から50%に下げました。これは、提供されるすべてのソリューションで非常に遅く実行されている可能性があります。


更新5

私は、MySQLへの移行が、私は同じ結果を取得しています。

+0

それはすなわち、これらの数字は、あなたのマシンの環境要因に依存していることは非常に可能です。ハードドライブの速度、.sqliteデータベースのサイズなどが含まれます。これらの値を投稿すると、ここで役立ちます。 –

+0

明日は投稿します。しかし、私にとって奇妙に見えるこれらのタイミングは、同じマシン上にあります。 –

+2

これが役立つかどうかは不明ですが、[R * Tree index](http://www.sqlite.org/rtree.html)を使用して調べましたか?彼らは効率的な範囲のクエリのために設計されています – Hasturkun

答えて

8

算術的にではなく、各テーブルの行数に幾何学的に比例する実行時間。

3 tables with 10 rows each => 1,000 comparision 

3 tables with 10, 10 and 40 rows => 4,000 comparisons 

3 tables with 20 rows each => 8,000 comparisons 

あなたはおそらく、いくつかの結合/カーソルを避けるためにクエリを再調整することができます。答えはいつ必要ですか? - あなたはすぐに非関連のスペクトルを検索する前に、不要な機能を、フィルタリングすることができますサブクエリを使用して

SELECT precursor_id, feature_table_id 
FROM MSMS_precursor 
INNER JOIN 

    (
     SELECT mzMin, mzMax, rtMin, rtMax, spectrum_id, feature_table_id, msrun_msrun_id 

     FROM spectrum 
     INNER JOIN 

      (select feature_table_id, mzMin, mzMax, rtMin, rtMax, msrun_msrun_id 
      from feature 
      where feature.msrun_msrun_id = 'value' 
      ) subquery 

     ON subquery.msrun_msrun_id = spectrum.msrun_msrun_id 
     WHERE 
      spectrum.scan_start_time BETWEEN subquery.rtMin AND subquery.rtMax 
    ) subquery 

    ON subquery.spectrum_id = MSMS_precursor.spectrum_spectrum_id 

WHERE 
    MSMS_precursor.ion_mz BETWEEN subquery.mzMin AND subquery.mzMax 

テーブル間の比較の回数を減らすことができます:

あなたはこのような何かを行うことができます適切な前駆体については、

私はSQLLiteを使用しませんが、原則はまだ適用されます。 UPDATED

:SQL

で固定バグ

注:

あなたは論理積を心配する必要はありません、あなただけ取得します:feature.msrun_msrun_id =

  • 機能を'value'
  • これらのフィーチャのスペクトル。スペクトラム.scan_start_time BETWEEN subquery.rtMin ANDサブクエリ。RTMAX
  • それらspectrsのための前駆体とsubquery.mzMinと subquery.mzMax BETWEEN MSMS_precursor.ion_mz

がUPDATE 18 /月:

それはインデックスです!!!あなたは、検索フィールドにインデックスを持ってではなく、参加するには参加してフィールドに - 外部キーのインデックスは本当にパフォーマンス向上:

CREATE INDEX `fk_msrun_msrun_id_feature` ON `feature` (`msrun_msrun_id` ASC); 
CREATE INDEX `fk_spectrum_spectrum_id_feature` ON `feature` (`msrun_msrun_id` ASC); 
CREATE INDEX `fk_spectrum_spectrum_id_MSMS_precursor` ON `MSMS_precursor` (`spectrum_spectrum_id` ASC); 
+0

これを動作させるのが難しいです。 mzMin、mzMax、rtMin、rtMaxはすべてフィーチャテーブルにあるので、どのようにrtMin/rtMaxやmzMin/mzMaxをサブクエリすることはできません。 2人の間にANDがあるので、1人が失敗すれば、もう一方の人をチェックしませんでしょうか? –

+0

私はselectステートメントを修正しました - 追加の注釈 –

+0

外部キーの周りのテーブルをインデックス化しようとしましたか? –

3

私は彼らは、効率的な範囲クエリのために設計されている、あなたはR*Tree indexを使用してみてくださいお勧めします。


実際にR * Treeはあまり使われていませんが、ドキュメントを読むだけですが、誤って使用している可能性があります。あなたの現在のクエリと同等でなければなりません

WHERE convexhull_edges.rtMin <= spectrum.scan_start_time AND convexhull_edges.rtMax >= spectrum.scan_start_time AND 
convexhull_edges.mzMin <= MSMS_precursor.ion_mz AND convexhull_edges.mzMax >= MSMS_precursor.ion_mz 

を使用するようにクエリを変更しようとする場合がありますが、私はより速く(あなたはR *木、外の範囲を選ぶのではなく、比較する必要がありますされるべきだと思います範囲を指す)

1

クエリに含まれるテーブルにcovering indicesを使用することを検討してください。

実際にselectステートメントと対応するinner joinおよびwhere句では、限られた数の列がフェッチされています。カバリングインデックスを使用して列を整えれば、search table using a covering indexのように、scan tableが削除されます。

は、あなたのテーブルの上にこれらのインデックスを使用してみてください:スピードのために行くとき

CREATE INDEX `fk_covering_feature` ON `feature` (`msrun_msrun_id`,`mzMin`,`mzMax`,`rtMin`,`rtMax`,`feature_table_id`); 
CREATE INDEX `fk_covering_spectrum` ON `spectrum` (`msrun_msrun_id`,`scan_start_time`,`spectrum_id`); 
CREATE INDEX `fk_covering_MSMS_precursor` ON `MSMS_precursor` (`spectrum_spectrum_id`,`ion_mz`,`precursor_id`); 

、あなたもmsrun_msrun_idが両方featurespectrumテーブルをチェックするために、一定であることを理解するために、クエリプランナをヒントすべきです。クエリの最後に、この追加のテストを置くことによって、あなたのクエリ内の一定のテストを追加します(二回spectrumFeature_InputValuesを渡す):

"AND spectrum.msrun_msrun_id = ?" 
関連する問題