Kueri dapat berfungsi seperti ini:
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;
Pertama , ambil value
"terbaru" untuk name = 'score'
per artikel di subkueri m
. Penjelasan lebih lanjut untuk teknik yang digunakan dalam jawaban terkait ini:
Anda tampaknya menjadi korban kesalahpahaman yang sangat mendasar:
tidak ada "tatanan alam" dalam sebuah tabel. Dalam SELECT
, Anda perlu ORDER BY
kriteria yang ditentukan dengan baik. Untuk tujuan kueri ini, saya mengasumsikan kolom metrics.date_created
. Jika Anda tidak memiliki hal semacam itu, Anda tidak mungkin untuk mendefinisikan "paling baru" dan dipaksa kembali ke pilihan arbitrer dari beberapa baris kualifikasi:
ORDER BY article_id
Ini bukan dapat diandalkan. Postgres akan memilih baris seperti yang dipilihnya. Dapat berubah dengan pembaruan apa pun pada tabel atau perubahan apa pun dalam rencana kueri.
Berikutnya , LEFT JOIN
ke tabel article
dan ORDER BY value
. NULL
diurutkan terakhir, jadi artikel tanpa nilai kualifikasi menjadi yang terakhir.
Catatan:beberapa ORM yang tidak terlalu pintar (dan saya khawatir ActiveRecord Ruby adalah salah satunya) menggunakan id
yang tidak deskriptif dan tidak khas sebagai nama untuk kunci utama. Anda harus menyesuaikan dengan nama kolom yang sebenarnya, yang tidak Anda berikan.
Kinerja
Harus layak. Ini adalah kueri "sederhana" sejauh menyangkut Postgres. Indeks multikolom parsial pada tabel metrics
akan membuatnya lebih cepat:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';
Kolom dalam urutan ini. Di PostgreSQL 9.2+ Anda dapat menambahkan nilai kolom untuk memungkinkan pemindaian hanya indeks:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';