Forespørgslen kunne fungere sådan her:
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;
Først , hent den "seneste" value
for name = 'score'
pr. artikel i underforespørgslen m
. Mere forklaring på den anvendte teknik i dette relaterede svar:
Du ser dog ud til at blive offer for en meget grundlæggende misforståelse:
Der er ingen "naturlig orden" i en tabel. I en SELECT
, skal du ORDER BY
veldefinerede kriterier. Til formålet med denne forespørgsel antager jeg en kolonne metrics.date_created
. Hvis du ikke har noget af den slags, har du ingen måde for at definere "seneste" og er tvunget til at falde tilbage til et vilkårligt valg fra flere kvalificerende rækker:
ORDER BY article_id
Dette er ikke pålidelig. Postgres vil vælge en række, som den vælger. Kan ændre sig med enhver opdatering af tabellen eller enhver ændring i forespørgselsplanen.
Næste , LEFT JOIN
til tabellen article
og ORDER BY value
. NULL
sorterer sidst, så artikler uden kvalificerende værdi kommer sidst.
Bemærk:nogle ikke-så-smarte ORM'er (og jeg er bange for, at Ruby's ActiveRecord er en af dem) bruger det ikke-beskrivende og ikke-særprægede id
som navn for den primære nøgle. Du bliver nødt til at tilpasse dig dine faktiske kolonnenavne, som du ikke har angivet.
Ydeevne
Bør være anstændigt. Dette er en "simpel" forespørgsel, hvad Postgres angår. Et delvist indeks med flere kolonner på tabel metrics
ville gøre det hurtigere:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';
Kolonner i denne rækkefølge. I PostgreSQL 9.2+ kan du tilføje kolonneværdien for at gøre indeks-scanninger mulige:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';