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

Ordine con una relazione has_many

La query potrebbe funzionare in questo modo:

SELECT a.*
FROM   article a
LEFT   JOIN (
   SELECT DISTINCT ON (article_id)
          article_id, value
   FROM   metrics m
   WHERE  name = 'score'
   ORDER  BY article_id, date_created DESC
   ) m ON m.metrics_id = a.metrics_id
ORDER  BY m.value DESC;

Prima , recupera il value "più recente". per name = 'score' per articolo nella sottoquery m . Ulteriori spiegazioni per la tecnica utilizzata in questa risposta correlata:

Tuttavia, sembri essere vittima di un malinteso di base:

Non esiste nessun "ordine naturale" in un tavolo. In un SELECT , devi ORDER BY criteri ben definiti. Ai fini di questa query sto assumendo una colonna metrics.date_created . Se non hai niente del genere, non hai alcuna strada per definire "più recente" e sono costretti a ricorrere a una scelta arbitraria da più righe di qualificazione:

   ORDER  BY article_id

Questo non affidabile. Postgres sceglierà una riga come preferisce. Può cambiare con qualsiasi aggiornamento della tabella o qualsiasi modifica nel piano di query.

Avanti , LEFT JOIN alla tabella article e ORDER BY value . NULL ordina per ultimi, quindi gli articoli senza valore qualificante vanno per ultimi.

Nota:alcuni ORM non così intelligenti (e temo che ActiveRecord di Ruby sia uno di questi) utilizzano l'id non descrittivo e non distintivo come nome per la chiave primaria. Dovrai adattarti ai nomi delle colonne effettivi, che non hai fornito.

Prestazioni

Dovrebbe essere decente. Questa è una query "semplice" per quanto riguarda Postgres. Un indice multicolonna parziale sulla tabella metrics lo renderebbe più veloce:

CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';

Colonne in questo ordine. In PostgreSQL 9.2+ puoi aggiungere il valore della colonna per rendere possibili scansioni solo indice:

CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';