PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Optimiser la requête avec OFFSET sur une grande table

Un grand OFFSET va toujours être lent. Postgres doit ordonner toutes les lignes et compter les visibles ceux jusqu'à votre décalage. Pour ignorer toutes les lignes précédentes directement vous pouvez ajouter un row_number indexé au tableau (ou créer une MATERIALIZED VIEW y compris ledit row_number ) et travaillez avec WHERE row_number > x au lieu de OFFSET x .

Cependant, cette approche n'est judicieuse que pour les données en lecture seule (ou principalement). Implémentation de la même chose pour les données de table qui peuvent changer simultanément est plus difficile. Vous devez commencer par définir exactement le comportement souhaité .

Je suggère une approche différente pour la pagination :

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 et id_x sont de la dernière rangée de la page précédente (pour les deux DESC et ASC ). Ou du premier si vous naviguez vers l'arrière .

La comparaison des valeurs de ligne est prise en charge par l'index que vous avez déjà - une fonctionnalité conforme à la norme ISO SQL, mais tous les SGBDR ne la prennent pas en charge.

CREATE INDEX vote_order_asc ON big_table (vote, id);

Ou pour l'ordre décroissant :

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

Peut utiliser le même index.
Je vous suggère de déclarer vos colonnes NOT NULL ou familiarisez-vous avec le NULLS FIRST|LAST construire :

  • PostgreSQL trier par datetime asc, null en premier ?

Notez deux choses en particulier :

  1. Le ROW valeurs dans WHERE La clause ne peut pas être remplacée par des champs de membre séparés. WHERE (vote, id) > (vote_x, id_x) ne peut pas être remplacé par :

    WHERE  vote >= vote_x
    AND    id   > id_x

    Cela exclurait tous lignes avec id <= id_x , alors que nous ne voulons le faire que pour le même vote et pas pour le suivant. La traduction correcte serait :

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

    ... qui ne s'adapte pas aussi bien aux index et devient de plus en plus compliqué pour plus de colonnes.

    Ce serait simple pour un célibataire colonne, évidemment. C'est le cas particulier que j'ai mentionné au début.

  2. La technique ne fonctionne pas pour les directions mixtes dans ORDER BY comme :

    ORDER  BY vote ASC, id DESC
    

    Au moins, je ne peux pas penser à un générique moyen de le mettre en œuvre aussi efficacement. Si au moins une des deux colonnes est de type numérique, vous pouvez utiliser un index fonctionnel avec une valeur inversée sur (vote, (id * -1)) - et utiliser la même expression dans ORDER BY :

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

Connexe :

  • Terme de syntaxe SQL pour 'WHERE (col1, col2) <(val1, val2)'
  • Améliorer les performances du tri par avec des colonnes provenant de nombreux tableaux

Notez en particulier la présentation de Markus Win et j'ai lié à :

  • "Pagination faite à la manière de PostgreSQL"