1

私は非常に大きなデータベースを持っています ( contacts~30 億のエントリがあり、people~2 億 8000 万のエントリがあり、他のテーブルには無視できる数のエントリがあります)。私が実行した他のほとんどのクエリは非常に高速です。しかし、非常に遅い、より複雑なクエリに遭遇しました。これをスピードアップする方法があるかどうか疑問に思っています。

まず、私のスキーマは次のとおりです。

CREATE TABLE activities (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE contacts (
        id INTEGER PRIMARY KEY,
        person1_id INTEGER NOT NULL,
        person2_id INTEGER NOT NULL,
        duration REAL NOT NULL, -- hours
        activity_id INTEGER NOT NULL
    --  FOREIGN_KEY(person1_id) REFERENCES people(id),
    --  FOREIGN_KEY(person2_id) REFERENCES people(id)
    );
CREATE TABLE people (
        id INTEGER PRIMARY KEY,
        state_id INTEGER NOT NULL,
        county_id INTEGER NOT NULL,
        age INTEGER NOT NULL,
        gender TEXT NOT NULL, -- M or F
        income INTEGER NOT NULL
    --  FOREIGN_KEY(state_id) REFERENCES states(id)
    );
CREATE TABLE states (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        abbreviation TEXT NOT NULL
    );
CREATE INDEX activities_name_index on activities(name);
CREATE INDEX contacts_activity_id_index on contacts(activity_id);
CREATE INDEX contacts_duration_index on contacts(duration);
CREATE INDEX contacts_person1_id_index on contacts(person1_id);
CREATE INDEX contacts_person2_id_index on contacts(person2_id);
CREATE INDEX people_age_index on people(age);
CREATE INDEX people_county_id_index on people(county_id);
CREATE INDEX people_gender_index on people(gender);
CREATE INDEX people_income_index on people(income);
CREATE INDEX people_state_id_index on people(state_id);
CREATE INDEX states_abbreviation_index on states(abbreviation);
CREATE INDEX states_name_index on states(name);

データベースのすべての列にインデックスを作成したことに注意してください。データベースのサイズは気にしません。速度は私が気にするすべてです。

予想どおり、ほぼ瞬時に実行されるクエリの例を次に示します。

SELECT count(*) FROM people, states WHERE people.state_id=states.id and states.abbreviation='IA';

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

SELECT * FROM contacts WHERE rowid IN
    (SELECT contacts.rowid FROM contacts, people, states
        WHERE contacts.person1_id=people.id AND people.state_id=states.id AND states.name='Kansas'
            INTERSECT
    SELECT contacts.rowid FROM contacts, people, states
        WHERE contacts.person2_id=people.id AND people.state_id=states.id AND states.name='Missouri');

さて、私が思うに、各サブクエリは、作成した関連する各インデックスを使用して、これを高速化します。ただし、クエリ プランを表示すると、次のように表示されます。

sqlite> EXPLAIN QUERY PLAN SELECT * FROM contacts WHERE rowid IN (SELECT contacts.rowid FROM contacts, people, states WHERE contacts.person1_id=people.id AND people.state_id=states.id AND states.name='Kansas' INTERSECT SELECT contacts.rowid FROM contacts, people, states WHERE contacts.person2_id=people.id AND people.state_id=states.id AND states.name='Missouri');
0|0|0|SEARCH TABLE contacts USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)
0|0|0|EXECUTE LIST SUBQUERY 1
2|0|2|SEARCH TABLE states USING COVERING INDEX states_name_index (name=?) (~1 rows)
2|1|1|SEARCH TABLE people USING COVERING INDEX people_state_id_index (state_id=?) (~5569556 rows)
2|2|0|SEARCH TABLE contacts USING COVERING INDEX contacts_person1_id_index (person1_id=?) (~12 rows)
3|0|2|SEARCH TABLE states USING COVERING INDEX states_name_index (name=?) (~1 rows)
3|1|1|SEARCH TABLE people USING COVERING INDEX people_state_id_index (state_id=?) (~5569556 rows)
3|2|0|SEARCH TABLE contacts USING COVERING INDEX contacts_person2_id_index (person2_id=?) (~12 rows)
1|0|0|COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)

実際、投稿した最初のクエリのクエリ プランを表示すると、次のようになります。

sqlite> EXPLAIN QUERY PLAN SELECT count(*) FROM people, states WHERE people.state_id=states.id and states.abbreviation='IA';
0|0|1|SEARCH TABLE states USING COVERING INDEX states_abbreviation_index (abbreviation=?) (~1 rows)
0|1|0|SEARCH TABLE people USING COVERING INDEX people_state_id_index (state_id=?) (~5569556 rows)

そして最後に、私が作成したインデックスの 1 つを使用して、それらが使用されることを証明するクエリを次に示します。

SELECT contacts.* FROM contacts, people, states WHERE contacts.person1_id=people.id AND people.state_id=states.id AND states.name='Iowa';

そのクエリは、次のクエリ プランを生成します。

sqlite> EXPLAIN QUERY PLAN SELECT contacts.* FROM contacts, people, states WHERE contacts.person1_id=people.id AND people.state_id=states.id AND states.name='Iowa';
0|0|2|SEARCH TABLE states USING COVERING INDEX states_name_index (name=?) (~1 rows)
0|1|1|SEARCH TABLE people USING COVERING INDEX people_state_id_index (state_id=?) (~5569556 rows)
0|2|0|SEARCH TABLE contacts USING INDEX contacts_person1_id_index (person1_id=?) (~12 rows)

私が作成したインデックスではなく、SQLite がカバリング インデックスを使用するのはなぜですか? これは正しい動作ですか?

4

1 に答える 1

3

Larry のおかげで、カバーリング インデックスとは何かを単純に誤解していました。SQLite私が作成したインデックスを使用しています。シャワーを浴びながら考えてみると、データベースが非常に大きいため、答えを生成するために必要な操作の数が非常に多いためだと思います。

カンザスには約 260 万人がいます。ミズーリ州の人口は約 540 万人です。person1 がカンザス州にいる連絡先を選択するには、260 万 * log(30 億) = 260 万 * 10 = 2600 万回のルックアップが必要です。次に、person2 がミズーリ州にいる連絡先を見つけるために、さらに 5,400 万回のルックアップを行う必要があります。

sqlite> SELECT count(*) FROM contacts, people, states WHERE contacts.person1_id=people.id AND people.state_id=states.id AND states.name='Kansas';
31665994
sqlite> SELECT count(*) FROM contacts, people, states WHERE contacts.person2_id=people.id AND people.state_id=states.id AND states.name='Missouri';
69436970

サイズが ~3170 万のセットとサイズが ~7000 万のセットの間で、セットの交差を実行する必要があります。

したがって、速度低下はデータベースやクエリの設計とは関係ありません。それは単にやるべきことがたくさんあります。

于 2012-10-05T04:47:09.433 に答える