PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ottimizza la query massima per gruppo

Supponendo relativamente pochi righe in options per molte righe in records .

In genere, dovresti cercare una tabella options a cui fa riferimento records.option_id , idealmente con un vincolo di chiave esterna. In caso contrario, suggerisco di crearne uno per rafforzare l'integrità referenziale:

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;

Quindi non c'è più bisogno di emulare una scansione dell'indice sciolto e questo diventa molto semplice e veloce . Le sottoquery correlate possono utilizzare un semplice indice su (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;

Ciò include le opzioni senza corrispondenza nella tabella records . Ottieni NULL per max_id e puoi facilmente rimuovere tali righe in un SELECT esterno se necessario.

Oppure (stesso risultato):

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;

Potrebbe essere leggermente più veloce. La sottoquery utilizza l'ordinamento DESC NULLS LAST - come la funzione aggregata max() che ignora i valori NULL. Ordinando solo DESC prima avrebbe NULL:

  • Perché i valori NULL vengono prima quando si ordina DESC in una query PostgreSQL?

L'indice perfetto per questo:

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

L'ordinamento degli indici non ha molta importanza mentre le colonne sono definite NOT NULL .

Potrebbe esserci ancora una scansione sequenziale sulla piccola tabella options , questo è solo il modo più veloce per recuperare tutte le righe. Il ORDER BY può portare a una scansione (solo) dell'indice per recuperare le righe preordinate.
La tabella grande records è accessibile solo tramite scansione dell'indice (bitmap) o, se possibile, scansione solo indice .

db<>gioca qui - mostra due scansioni solo indice per il caso semplice
Old sqlfiddle

Oppure usa LATERAL si unisce per un effetto simile in Postgres 9.3+:

  • Ottimizza la query GROUP BY per recuperare l'ultima riga per utente