PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Ordem com um relacionamento has_many


A consulta poderia funcionar assim:
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;

Primeiro , recupere o value "mais recente" para name = 'score' por artigo na subconsulta m . Mais explicações para a técnica usada nesta resposta relacionada:

Você parece ser vítima de um equívoco muito básico:

Não existe nenhuma "ordem natural" em uma mesa. Em um SELECT , você precisa ORDER BY critérios bem definidos. Para o propósito desta consulta, estou assumindo uma coluna metrics.date_created . Se você não tem nada do tipo, você não tem como para definir "mais recente" e são forçados a recorrer a uma escolha arbitrária de várias linhas de qualificação:
   ORDER  BY article_id

Isso não confiável. O Postgres escolherá uma linha conforme escolher. Pode mudar com qualquer atualização na tabela ou qualquer alteração no plano de consulta.

Próximo , LEFT JOIN para a tabela article e ORDER BY value . NULL classifica por último, então os artigos sem valor qualificável vão por último.

Nota:alguns ORMs não tão inteligentes (e temo que o ActiveRecord do Ruby seja um deles) usam o id não descritivo e não distintivo como nome para a chave primária. Você terá que se adaptar aos seus nomes de coluna reais, que você não forneceu.

Desempenho


Deve ser decente. Esta é uma consulta "simples" no que diz respeito ao Postgres. Um índice parcial de várias colunas na tabela metrics tornaria mais rápido:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';

Colunas nesta ordem. No PostgreSQL 9.2+, você pode adicionar o valor da coluna para possibilitar varreduras somente de índice:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';