sql - multiple - django select distinct on expressions must match initial order by expressions
PostgreSQL DISTINCT ON con diferentes ORDER BY (6)
La documentación dice:
DISTINCT ON (expresión [, ...]) mantiene solo la primera fila de cada conjunto de filas donde las expresiones dadas son iguales. [...] Tenga en cuenta que la "primera fila" de cada conjunto es impredecible a menos que se utilice ORDER BY para garantizar que la fila deseada aparezca primero. [...] Las expresiones DISTINCT ON deben coincidir con la (s) expresión (es) ORDER BY más a la izquierda.
Por lo tanto, tendrá que agregar la address_id
al pedido por.
Alternativamente, si está buscando la fila completa que contiene el producto comprado más reciente para cada address_id
y ese resultado ordenado por purchased_at
entonces está tratando de resolver un mayor problema de N por grupo que puede resolverse mediante los siguientes enfoques:
La solución general que debería funcionar en la mayoría de los DBMS:
SELECT t1.* FROM purchases t1
JOIN (
SELECT address_id, max(purchased_at) max_purchased_at
FROM purchases
WHERE product_id = 1
GROUP BY address_id
) t2
ON t1.address_id = t2.address_id AND t1.purchased_at = t2.max_purchased_at
ORDER BY t1.purchased_at DESC
Una solución más orientada a PostgreSQL basada en la respuesta de @ hkf:
SELECT * FROM (
SELECT DISTINCT ON (address_id) *
FROM purchases
WHERE product_id = 1
ORDER BY address_id, purchased_at DESC
) t
ORDER BY purchased_at DESC
Problema aclarado, extendido y resuelto aquí: Selección de filas ordenadas por una columna y distintas en otra
Quiero ejecutar esta consulta:
SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC
Pero me sale este error:
PG :: Error: ERROR: las expresiones SELECT DISTINCT ON deben coincidir con las expresiones ORDER BY iniciales
Agregar address_id
como primera expresión ORDER BY
silencia el error, pero realmente no quiero agregar sorting sobre address_id
. ¿Es posible hacer sin ordenar por address_id
?
La función de ventana puede resolver eso en una pasada:
SELECT DISTINCT ON (address_id)
LAST_VALUE(purchases.address_id) OVER wnd AS address_id
FROM "purchases"
WHERE "purchases"."product_id" = 1
WINDOW wnd AS (
PARTITION BY address_id ORDER BY purchases.purchased_at DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
Para cualquiera que use Flask-SQLAlchemy, esto funcionó para mí
from app import db
from app.models import Purchases
from sqlalchemy.orm import aliased
from sqlalchemy import desc
stmt = Purchases.query.distinct(Purchases.address_id).subquery(''purchases'')
alias = aliased(Purchases, stmt)
distinct = db.session.query(alias)
distinct.order_by(desc(alias.purchased_at))
Puede ordenar por dirección_id en una subconsulta, luego ordenar según lo que desee en una consulta externa.
SELECT * FROM
(SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM "purchases"
WHERE "purchases"."product_id" = 1 ORDER BY address_id DESC )
ORDER BY purchased_at DESC
También puedes hacer esto usando la cláusula group by
SELECT purchases.address_id, purchases.* FROM "purchases"
WHERE "purchases"."product_id" = 1 GROUP BY address_id,
purchases.purchased_at ORDER purchases.purchased_at DESC
Una subconsulta puede resolverlo:
SELECT *
FROM (
SELECT DISTINCT ON (address_id) *
FROM purchases
WHERE product_id = 1
) p
ORDER BY purchased_at DESC;
Las expresiones principales en ORDER BY
deben estar de acuerdo con las columnas en DISTINCT ON
, por lo que no puede ordenar por columnas diferentes en el mismo SELECT
.
Solo use un ORDER BY
adicional en la subconsulta si desea seleccionar una fila particular de cada conjunto:
SELECT *
FROM (
SELECT DISTINCT ON (address_id) *
FROM purchases
WHERE product_id = 1
ORDER BY address_id, purchased_at DESC -- get "latest" row per address_id
) p
ORDER BY purchased_at DESC;
Si purchased_at
puede ser NULL
, considere DESC NULLS LAST
.
Relacionado, con más explicaciones: