it-swarm.com.de

PostgreSQL DISTINCT ON mit verschiedenen ORDER BY

Ich möchte diese Abfrage ausführen:

SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC

Aber ich bekomme diesen Fehler:

PG :: Error: ERROR: SELECT DISTINCT ON-Ausdrücke müssen mit den anfänglichen ORDER BY-Ausdrücken übereinstimmen

Wenn Sie address_id Als ersten Ausdruck ORDER BY Hinzufügen, wird der Fehler behoben, aber ich möchte wirklich keine Sortierung über address_id Hinzufügen. Kann man auf eine Bestellung mit address_id Verzichten?

177
sl_bug

Dokumentation sagt:

DISTINCT ON (Ausdruck [ ...]) behält nur die erste Zeile jeder Reihe von Zeilen bei, in denen die angegebenen Ausdrücke als gleich ausgewertet werden. [...] Beachten Sie, dass die "erste Zeile" jedes Satzes nicht vorhersehbar ist, es sei denn, ORDER BY wird verwendet, um sicherzustellen, dass die gewünschte Zeile zuerst angezeigt wird. [...] Die DISTINCT ON-Ausdrücke müssen mit den am weitesten links stehenden ORDER BY-Ausdrücken übereinstimmen.

Offizielle Dokumentation

Sie müssen also den address_id Zur Bestellung von hinzufügen.

Wenn Sie alternativ nach der vollständigen Zeile suchen, die das zuletzt gekaufte Produkt für jedes address_id Und das nach purchased_at Sortierte Ergebnis enthält, versuchen Sie, ein größtes N-pro-Gruppe-Problem zu lösen die durch die folgenden Ansätze gelöst werden können:

Die allgemeine Lösung, die in den meisten DBMS funktionieren sollte:

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

Eine stärker auf PostgreSQL ausgerichtete Lösung, die auf der Antwort von @ hkf basiert:

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

Hier wird das Problem geklärt, erweitert und gelöst: Auswählen von Zeilen, die nach einer Spalte sortiert sind und sich von einer anderen unterscheiden

169
Mosty Mostacho

Sie können in einer Unterabfrage nach address_id und in einer äußeren Abfrage nach Ihren Wünschen sortieren.

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
51
hkf

Eine Unterabfrage kann es lösen:

SELECT *
FROM  (
    SELECT DISTINCT ON (address_id) *
    FROM   purchases
    WHERE  product_id = 1
    ) p
ORDER  BY purchased_at DESC;

Führende Ausdrücke in ORDER BY Müssen mit Spalten in DISTINCT ON Übereinstimmen, sodass Sie nicht nach verschiedenen Spalten in demselben SELECT sortieren können.

Verwenden Sie in der Unterabfrage nur einen zusätzlichen ORDER BY, Wenn Sie eine bestimmte Zeile aus jedem Satz auswählen möchten:

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;

Wenn purchased_atNULL sein kann, ziehen Sie DESC NULLS LAST In Betracht.
Verwandte, mit mehr Erklärung:

37

Die Fensterfunktion kann das in einem Durchgang lösen:

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)
10
savenkov

Für alle, die Flask-SQLAlchemy verwenden, hat dies bei mir funktioniert

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))
4
reubano
SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY address_id, purchases.purchased_at DESC

ORDER BY address_id, purchase.purchased_at DESC

für die Funktion DISTINCT ON () muss address_id in der Reihenfolge von hinzugefügt werden

0
REMITH