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

Optimiser la requête maximale par groupe

En supposant relativement peu lignes dans options pour plusieurs lignes dans records .

Typiquement, vous auriez une recherche table options qui est référencé à partir de records.option_id , idéalement avec une contrainte de clé étrangère. Si ce n'est pas le cas, je vous suggère d'en créer un pour renforcer l'intégrité référentielle :

CREATE TABLE options (
  option_id int  PRIMARY KEY
, option    text UNIQUE NOT NULL
);

INSERT INTO options
SELECT DISTINCT option_id, 'option' || option_id -- dummy option names
FROM   records;

Ensuite, il n'est plus nécessaire d'émuler un balayage d'index lâche et cela devient très simple et rapide . Les sous-requêtes corrélées peuvent utiliser un index simple sur (option_id, id) .

SELECT option_id, (SELECT max(id)
                   FROM   records
                   WHERE  option_id = o.option_id) AS max_id
FROM   options o
ORDER  BY 1;

Cela inclut les options sans correspondance dans la table records . Vous obtenez NULL pour max_id et vous pouvez facilement supprimer ces lignes dans un SELECT externe si nécessaire.

Ou (même résultat) :

SELECT option_id, (SELECT id
                   FROM   records
                   WHERE  option_id = o.option_id
                   ORDER  BY id DESC NULLS LAST
                   LIMIT  1) AS max_id
FROM   options o
ORDER  BY 1;

Peut être légèrement plus rapide. La sous-requête utilise l'ordre de tri DESC NULLS LAST - identique à la fonction d'agrégation max() qui ignore les valeurs NULL. Tri uniquement DESC aurait NULL en premier :

  • Pourquoi les valeurs NULL sont-elles prioritaires lors de l'ordre de DESC dans une requête PostgreSQL ?

L'index parfait pour cela :

CREATE INDEX on records (option_id, id DESC NULLS LAST);

L'ordre de tri de l'index n'a pas beaucoup d'importance tant que les colonnes sont définies NOT NULL .

Il peut toujours y avoir un parcours séquentiel sur la petite table options , c'est simplement le moyen le plus rapide de récupérer toutes les lignes. Le ORDER BY peut apporter une analyse d'index (uniquement) pour récupérer des lignes pré-triées.
La grande table records n'est accessible que par balayage d'index (bitmap) ou, si possible, balayage d'index uniquement .

db<>jouez ici - montrant deux analyses d'index uniquement pour le cas simple
Ancien sqlfiddle

Ou utilisez LATERAL se joint pour un effet similaire dans Postgres 9.3+ :

  • Optimiser la requête GROUP BY pour récupérer la dernière ligne par utilisateur