2011-10-25 6 views
10

非常に大きなテーブルのSCANを常に引き起こすサブクエリを含むクエリがあり、その結果クエリ時間が悪くなります。静的リストがないときにサブクエリがスキャンを実行するのはなぜですか?

これは私が使用しているクエリです:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (
        SELECT PersonId 
         FROM relationship 
        WHERE RelatedToPersonId = 12270351721 
        ); 

をクエリプランは次のように報告されます。

SCAN TABLE person (~100000 rows) 
EXECUTE LIST SUBQUERY 1 
SEARCH TABLE relationship USING INDEX relationship_RelatedToPersonId_IDX (RelatedToPersonId=?) (~10 rows) 

(サブクエリの結果に相当)の静的リストと同じクエリを:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

そして、そのためのクエリプラン:

SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (PersonId=?) (~5 rows) 
EXECUTE LIST SUBQUERY 1 

なぜ、最初のクエリで2番目のクエリがスキャンされないのですか?

+0

サブクエリのデータセットが小さい場合、一時テーブルが機能する可能性があります。次のようなものがあります。 Personid Fromの選択(SELECT PersonId FROM relationship WHERE RelatedToPersonId = 12270351721); – DallinDyer

+0

私は一時テーブルを試したが、それでもスキャンを生成した。 – goto10

+0

'person'に行がない場合、' PersonId'が 'relationship'に存在しますか?そうでない場合、サブクエリ(おそらく 'distinct'を持つ)がクエリ全体を満足するのではないでしょうか? –

答えて

2

サブクエリのWHERE句に別のフィールド(RelatedToPersonId)が含まれているため、テーブルをスキャンする必要があります。 PersonIDのリストは、直接インデックスに行くことができます。

+0

RelatedToPersonIdが関係テーブルにあります。なぜそれが人のテーブルをスキャンするのでしょうか?そのサブクエリが独立して実行されるべきではなく、結果リストが生成され、静的な値リストを持つクエリと同じように動作する必要がありますか?そのクエリのクエリプランを見ると、サブクエリ部分が実際にインデックスに当たっています。 – goto10

+0

SQLiteオプティマイザは、サブクエリがインデックスを使用できることを確実に知っている必要があります。最初に実行してから、personテーブルの行をその結果セットだけに制限してください。 – jjxtra

0

この:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

は、このための単なるシンタックスシュガーです:

SELECT PersonId 
    FROM person 
WHERE (
     PersonId = 12270351727 
     OR PersonId = 12270351730 
     OR PersonId = 12270367969 
     OR PersonId = 12387741400 
     ); 
+0

はい、サブクエリが採用されたときにスキャンが必要な理由、さらに重要なのはそのスキャンを回避する方法については説明しません。この場合、SQLiteクエリオプティマイザが最適なクエリプランを生成していないようです。 – goto10

1

私は再現することはできません。私は新鮮なSQLiteバージョン3.7.8がインストールに次のステートメントを実行:

create table person (id int not null primary key, name varchar(20)); 
insert into person values (1, 'a'); 
insert into person select id + 1, name || 'b' from person; 
insert into person select id + 2, name || 'c' from person; 
insert into person select id + 4, name || 'd' from person; 
insert into person select id + 8, name || 'e' from person; 
insert into person select id + 16, name || 'f' from person; 
insert into person select id + 32, name || 'g' from person; 
insert into person select id + 64, name || 'h' from person; 
insert into person select id + 128, name || 'i' from person; 
insert into person select id + 256, name || 'j' from person; 
insert into person select id + 512, name || 'k' from person; 
insert into person select id + 512, name || 'l' from person; 
insert into person select id + 1024, name || 'l' from person; 
insert into person select id + 2048, name || 'm' from person; 
insert into person select id + 4096, name || 'n' from person; 
insert into person select id + 8192, name || 'o' from person; 
insert into person select id + 16384, name || 'p' from person; 
insert into person select id + 32768, name || 'q' from person; 
insert into person select id + 65536, name || 'r' from person; 
select count(*) from person; 

create table relation (id int, related int); 
insert into relation select id, id + 1 from person; 
insert into relation select id, id + 2 from person; 
insert into relation select id, id + 3 from person; 
insert into relation select id, id + 4 from person; 
insert into relation select id, id + 5 from person; 
insert into relation select id, id + 6 from person; 
insert into relation select id, id + 7 from person; 
insert into relation select id, id + 8 from person; 
insert into relation select id, id + 9 from person; 
insert into relation select id, id + 10 from person; 
delete from relation where related not in (select id from person); 

create index relatedToPerson on relation(related); 
explain query plan select id from person 
    where id in (select id from relation where related = 2345); 

クエリプラン文の結果:

0|0|0|SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (id=?)(~25 rows) 
0|0|0|EXECUTE LIST SUBQUERY 1 
1|0|0|SEARCH TABLE relation USING INDEX relatedToPerson (related=?) (~10 rows) 

は、なぜそれがあなたのために動作しませんか?私は考えることができるの理由:

  • 列PERSONIDが含まれていないあなたのテーブルの関係( は確認してください)あなたはSQLiteの別のバージョンを使用している
  • あなたは、例えば、ユニークインデックスの他の制約を持っています。

上記のスクリプトを実行して、同じ結果が得られるかどうかを確認できますか?

+0

私は今週これで忙しくて明日町に出るので、もう少し時間をとることができないかもしれません。しかし、私は恩恵を無駄にしたくないので、あなたが入れている努力のためにそれをあなたに授与しています。あなたの例が真にスキャンを引き起こさないなら、それはなぜ私が良い理由を理解しようとしているか私の仕事は同じではありません。 – goto10

+0

幸運。結果をここに投稿してください。私はその違いが何か不思議です。 – boes

+0

データタイプの不一致(テキストと整数) – DallinDyer

1

なぜテーブルスキャンがあるのか​​わかりません。あなたも参加し使用することができます:しかし、そこここに副選択を使用する必要はありませんsqliteのは、この結合構文をサポートしている場合

SELECT person.PersonId 
    FROM person JOIN relationship ON person.PersonId = relationship.PersonId 
WHERE RelatedToPersonId = 12270351721; 

わからないが、これはまた、使用して書き換えることができFROMとWHERE:

SELECT person.PersonId 
    FROM person, relationship 
WHERE person.PersonId = relationship.PersonId 
    AND RelatedToPersonId = 12270351721; 

さらに、これはオプティマイザのチューニングの詳細です。

+0

上記のコメントで説明したように、これは予期しないスキャンの非常に単純なケースを示す高度のクエリです。 「実生活」のクエリは実質的に複雑ですが、同じ状況に陥ります。つまり、静的リストに同じ値を入れてスキャンを生成する短いリストを生成するサブクエリは、スキャンを引き起こしません。 – goto10

2

おそらく比類のないデータ型です。 person.PersonIdとrelationship.PersonIdは同じデータ定義を持っていますか?

+1

右に。ありがとう! – DallinDyer

0

personの行ごとにサブクエリが実行されるため、フルスキャンが発生します。サブクエリは、結果セットを "キャッシュ"してINに変換されません。

希望します。

関連する問題