PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Optimieren Sie die gruppenweise maximale Abfrage

Angenommen relativ wenige Zeilen in options für viele Zeilen in records .

Normalerweise haben Sie eine Nachschlagetabelle Tabelle options auf die von records.option_id verwiesen wird , idealerweise mit einer Fremdschlüsseleinschränkung. Wenn Sie dies nicht tun, schlage ich vor, eine zu erstellen, um die referenzielle Integrität zu erzwingen:

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;

Dann muss kein loser Index-Scan mehr emuliert werden und dies wird sehr einfach und schnell . Korrelierte Unterabfragen können einen einfachen Index für (option_id, id) verwenden .

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

Dazu gehören Optionen ohne Übereinstimmung in den records der Tabelle . Sie erhalten NULL für max_id und Sie können solche Zeilen einfach in einem äußeren SELECT entfernen bei Bedarf.

Oder (gleiches Ergebnis):

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;

Kann etwas schneller sein. Die Unterabfrage verwendet die Sortierreihenfolge DESC NULLS LAST - wie die Aggregatfunktion max() die NULL-Werte ignoriert. Sortieren Sie einfach DESC hätte zuerst NULL:

  • Warum kommen NULL-Werte zuerst, wenn DESC in einer PostgreSQL-Abfrage bestellt wird?

Der perfekte Index dafür:

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

Die Sortierreihenfolge der Indizes spielt keine große Rolle, solange Spalten als NOT NULL definiert sind .

Auf der kleinen Tabelle options kann immer noch ein sequentielles Scannen erfolgen , das ist nur der schnellste Weg, um alle Zeilen abzurufen. Der ORDER BY kann einen (nur) Index-Scan einbringen, um vorsortierte Zeilen abzurufen.
Die große Tabelle records wird nur über (Bitmap-)Index-Scan oder, wenn möglich, Index-Only-Scan zugegriffen .

db<>hier fummeln - zeigt zwei Nur-Index-Scans für den einfachen Fall
Old sqlfiddle

Oder Verwenden Sie LATERAL Joins für einen ähnlichen Effekt in Postgres 9.3+:

  • GRUPPE NACH-Abfrage optimieren, um die neueste Zeile pro Benutzer abzurufen