sql >> データベース >  >> RDS >> PostgreSQL

PostgreSQLのカウントクエリの最適化

    PostgreSQLは、実際には配列列のGINインデックスをサポートしています。残念ながら、NOT ARRAY[...] <@ indexed_colには使用できないようです。 、およびGIN とにかく、インデックスは頻繁に更新されるテーブルには適していません。

    デモ:

    CREATE TABLE arrtable (id integer primary key, array_column integer[]);
    
    INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
    
    CREATE INDEX arrtable_arraycolumn_gin_arr_idx
    ON arrtable USING GIN(array_column);
    
    -- Use the following *only* for testing whether Pg can use an index
    -- Do not use it in production.
    SET enable_seqscan = off;
    
    explain (buffers, analyze) select count(id) 
    from arrtable 
    where not (ARRAY[1] <@ arrtable.array_column);
    

    残念ながら、これは書かれているようにインデックスを使用できないことを示しています。条件を否定しない場合は、それを使用できるため、実行する行を検索してカウントできます。 検索要素を含める(NOTを削除する )。

    インデックスを使用して、実行するエントリをカウントできます。 ターゲット値を含み、すべてのエントリのカウントからその結果を減算します。 count以降 テーブル内のすべての行の実行はPostgreSQL(9.1以前)では非常に遅く、シーケンシャルスキャンが必要です。これは実際には現在のクエリよりも遅くなります。 idにb-treeインデックスがある場合、9.2ではインデックスのみのスキャンを使用して行をカウントできる可能性があります。 、この場合、これは実際には問題ない可能性があります:

    SELECT (
      SELECT count(id) FROM arrtable
    ) - (
      SELECT count(id) FROM arrtable 
      WHERE (ARRAY[1] <@ arrtable.array_column)
    );
    

    seqscanに加えて、オリジナルには も必要なため、Pg9.1以下のオリジナルバージョンよりもパフォーマンスが低下することが保証されています。 GINインデックススキャンが必要です。これを9.2でテストしましたが、カウントにインデックスを使用しているように見えるので、9.2で調べる価値があります。些細なダミーデータを使用:

    drop index arrtable_arraycolumn_gin_arr_idx ;
    truncate table arrtable;
    insert into arrtable (id, array_column)
    select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
    CREATE INDEX arrtable_arraycolumn_gin_arr_idx
    ON arrtable USING GIN(array_column);
    

    このようなGINインデックスは、更新の速度を大幅に低下させ、そもそも作成に非常に時間がかかることに注意してください。テーブルのように、大幅に更新されるテーブルには適していません。

    さらに悪いことに、このインデックスを使用するクエリは、元のクエリの最大2倍、最大で半分の長さです 同じデータセットで。 ARRAY[1]のように、インデックスがあまり選択的でない場合は最悪です。 -元のクエリの4秒と2秒。インデックスが非常に選択的である場合(つまり、ARRAY[199]のように一致するものが少ない場合 )オリジナルの3秒に対して約1.2秒で実行されます。このインデックスは、このクエリに使用する価値がありません。

    ここでのレッスンは?場合によっては、正しい答えは単にシーケンシャルスキャンを実行することです。

    ヒット率には影響しないため、@ debenhurが提案するようにトリガーを使用してマテリアライズドビューを維持するか、配列を反転して、エントリがしないパラメータのリストになるようにしてください。 @maniekが提案するようにGiSTインデックスを使用できるようにします。



    1. Java-UTF-8文字とUTF-8以外の文字が混在する文字列から正確に60文字をカウントします

    2. MySQL-caseステートメントを最適化する

    3. MySQLで午前6時前と午後10時以降の時間差を計算する

    4. RustのPostgreSQLからタイムゾーン(timestamptz)値のタイムスタンプを読み取るにはどうすればよいですか?