sql >> Base de Datos >  >> RDS >> PostgreSQL

Pedido con una relación has_many

La consulta podría funcionar así:

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;

Primero , recupera el value "más reciente" para name = 'score' por artículo en la subconsulta m . Más explicación de la técnica utilizada en esta respuesta relacionada:

Sin embargo, pareces ser víctima de un concepto erróneo muy básico:

No hay ningún "orden natural" en una mesa En un SELECT , necesitas ORDER BY criterios bien definidos. Para el propósito de esta consulta, asumo una columna metrics.date_created . Si no tienes nada por el estilo, no tienes de ninguna manera para definir "más reciente" y se ven obligados a recurrir a una selección arbitraria de varias filas de calificación:

   ORDER  BY article_id

Esto es no de confianza. Postgres elegirá una fila como elija. Puede cambiar con cualquier actualización de la tabla o cualquier cambio en el plan de consulta.

Siguiente , LEFT JOIN a la tabla article y ORDER BY value . NULL se clasifica en último lugar, por lo que los artículos sin valor de calificación van en último lugar.

Nota:algunos ORM no tan inteligentes (y me temo que ActiveRecord de Ruby es uno de ellos) utilizan el id no descriptivo ni distintivo. como nombre para la clave principal. Tendrá que adaptarse a los nombres de las columnas reales, que no proporcionó.

Rendimiento

Debería ser decente. Esta es una consulta "simple" en lo que respecta a Postgres. Un índice parcial de varias columnas en la tabla metrics lo haría más rápido:

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

Columnas en este orden. En PostgreSQL 9.2+, puede agregar el valor de la columna para hacer posibles los escaneos de solo índice:

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