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';