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

大きなテーブルでOFFSETを使用してクエリを最適化する

    大きなOFFSET 常に遅くなります。 Postgresはすべての行を並べ替え、表示をカウントする必要があります あなたのオフセットまでのもの。前のすべての行をスキップするには直接 インデックス付きのrow_numberを追加できます テーブルに(またはMATERIALIZED VIEWを作成します 上記のrow_numberを含む )そしてWHERE row_number > xで作業します OFFSET xの代わりに 。

    ただし、このアプローチは、読み取り専用(またはほとんど)のデータにのみ適しています。 同時に変更される可能性のあるテーブルデータにも同じように実装する より挑戦的です。目的の動作を正確に定義することから始める必要があります 。

    ページネーションには別のアプローチを提案します :

    SELECT *
    FROM   big_table
    WHERE  (vote, id) > (vote_x, id_x)  -- ROW values
    ORDER  BY vote, id  -- needs to be deterministic
    LIMIT  n;
    

    ここでvote_x およびid_x 最後からです 前のページの行 (両方のDESC およびASC )。または最初から 後方に移動する場合

    行の値の比較は、既存のインデックスによってサポートされています。これは、ISO SQL標準に準拠する機能ですが、すべてのRDBMSがサポートしているわけではありません。

    CREATE INDEX vote_order_asc ON big_table (vote, id);
    

    または降順の場合:

    SELECT *
    FROM   big_table
    WHERE  (vote, id) < (vote_x, id_x)  -- ROW values
    ORDER  BY vote DESC, id DESC
    LIMIT  n;
    

    同じインデックスを使用できます。
    列をNOT NULLとして宣言することをお勧めします または、NULLS FIRST|LASTをよく理解してください 構成:

    • PostgreSQLは日時ascで並べ替え、最初はnullですか?

    2つのことに注意してください 特に:

    1. ROW WHEREの値 句を個別のメンバーフィールドに置き換えることはできません。 WHERE (vote, id) > (vote_x, id_x) できません 次のように置き換えられます:

      WHERE  vote >= vote_x
      AND    id   > id_x

      それはすべてを除外します id <= id_xの行 、私たちは同じ投票のためだけにそれをしたいのですが、次の投票のためではありません。正しい翻訳は次のようになります:

      WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
      

      ...これはインデックスとうまく連携せず、列が増えるとますます複雑になります。

      シングルの場合は簡単です 明らかに、列。これは、最初に述べた特殊なケースです。

    2. この手法は、ORDER BYの混合方向では機能しません のように:

      ORDER  BY vote ASC, id DESC
      

      少なくとも私は一般的なを考えることはできません これを効率的に実装する方法。両方の列の少なくとも1つが数値タイプの場合、(vote, (id * -1))で値が反転した機能インデックスを使用できます。 -ORDER BYで同じ式を使用します :

      ORDER  BY vote ASC, (id * -1) ASC
      

    関連:

    • 'WHERE(col1、col2)<(val1、val2)'のSQL構文用語
    • 多くのテーブルの列を使用して注文のパフォーマンスを向上させる

    特に、私がリンクしたMarkusWinandによるプレゼンテーションに注意してください。

    • 「PaginationはPostgreSQLの方法で実行されました」


    1. Cloud9 postgres

    2. mysqlエラー1364フィールドにデフォルト値がありません

    3. 分散トランザクションを開始できません

    4. MariaDBのTRIM()とTRIM_ORACLE()の違い