大きな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つのことに注意してください 特に:
-
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
...これはインデックスとうまく連携せず、列が増えるとますます複雑になります。
シングルの場合は簡単です 明らかに、列。これは、最初に述べた特殊なケースです。
-
この手法は、
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の方法で実行されました」