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