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

Come faccio a scrivere un join con questi criteri di corrispondenza insoliti?

Indici

Crea indici su x.id e y.id - che probabilmente hai già se quelle sono le tue chiavi primarie.
Anche un indice a più colonne può aiutare, specialmente con solo indicizza scansioni a pagina 9.2+:

CREATE INDEX y_mult_idx ON y (id DESC, val)

Tuttavia, nei miei test, questo indice non è stato inizialmente utilizzato. Ho dovuto aggiungere (altrimenti inutile) val a ORDER BY per convincere il pianificatore di query che l'ordinamento corrisponde. Vedi query 3 .

L'indice fa poca differenza in questa configurazione sintetica. Ma per le tabelle con più colonne, recuperando val dal tavolo diventa sempre più costoso, rendendo più appetibile l'indice di "copertura".

Query

1) Semplice

SELECT DISTINCT ON (x.id)
       x.id, y.val
FROM   x
JOIN   y ON y.id <= x.id
ORDER  BY x.id, y.id DESC;

SQL Fiddle.

Maggiori spiegazioni per la tecnica con DISTINCT in questa risposta correlata:

Ho eseguito alcuni test perché sospettavo che la prima query non si sarebbe adattata bene. È veloce con un tavolino piccolo, ma non va bene con tavoli più grandi. Postgres non ottimizza il piano e inizia con un cross join (limitato), con un costo di O(N²) .

2) Veloce

Questa query è ancora piuttosto semplice e si adatta in modo eccellente:

SELECT x.id, y.val
FROM   x
JOIN  (SELECT *, lead(id, 1, 2147483647) OVER (ORDER BY id) AS next_id FROM y) y
       ON  x.id >= y.id
       AND x.id <  y.next_id
ORDER  BY 1;

La funzione della finestra lead() è strumentale. Uso l'opzione per fornire un valore predefinito per coprire il caso d'angolo dell'ultima riga:2147483647 è il numero intero più grande possibile . Adattati al tuo tipo di dati.

3) Molto semplice e quasi altrettanto veloce

SELECT x.id
     ,(SELECT val FROM y WHERE id <= x.id ORDER BY id DESC, val LIMIT 1) AS val
FROM   x;

Normalmente, sottoquery correlate tendono ad essere lenti. Ma questo può semplicemente scegliere un valore dall'indice (di copertura) ed è altrimenti così semplice da poter competere.

L'ulteriore ORDER BY voce val (enfasi in grassetto) sembra inutile. Ma aggiungendolo si convince il pianificatore di query che è possibile utilizzare l'indice a più colonne y_mult_idx dall'alto, perché l'ordinamento corrisponde. Nota il

nel EXPLAIN uscita.

Caso di prova

Dopo un vivace dibattito e molteplici aggiornamenti, ho raccolto tutte le domande pubblicate finora e ho creato un banco di prova per una rapida panoramica. Uso solo 1000 righe, quindi SQLfiddle non scade con le query più lente. Ma i primi 4 (Erwin 2, Clodoaldo, a_horse, Erwin 3) scalano linearmente in tutti i miei test locali. Aggiornato ancora una volta per includere la mia ultima aggiunta, migliora il formato e l'ordine in base alle prestazioni ora:

Big SQL Fiddle confrontare le prestazioni.