Eine Geige für meine Frage finden Sie unter https://dbfiddle.uk/?rdbms=postgres_10&fiddle=3cd9335fa07565960c1837aa65143685 .
Ich habe ein einfaches Tabellenlayout:
class
person: belongs to a class
Ich möchte alle Klassen auswählen und für jede Klasse möchte ich die ersten beiden Personenkennungen der zugehörigen Personen nach absteigendem Namen sortieren.
Ich habe dies mit der folgenden Abfrage gelöst:
select c.identifier, array_agg(p.identifier order by p.name desc) as persons
from class as c
left join lateral (
select p.identifier, p.name
from person as p
where p.class_identifier = c.identifier
order by p.name desc
limit 2
) as p
on true
group by c.identifier
order by c.identifier
Hinweis: Ich hätte eine Korrelationsunterabfrage in der SELECT
Klausel verwenden können, aber ich versuche, dies als Teil eines Lernprozesses zu vermeiden.
Wie Sie sehen, bewerbe ich mich order by p.name desc
an zwei Stellen:
- in der Unterabfrage
- in der Aggregatfunktion
Gibt es eine Möglichkeit, dies zu vermeiden? Mein Zug des Unterrichts:
Erstens kann ich das
order by
in der Unterabfrage natürlich nicht entfernen , da dies eine Abfrage ergeben würde, die meine oben angegebene Anforderung nicht erfüllt.Zweitens denke ich, dass die
order by
Funktion in der Aggregatfunktion nicht ausgelassen werden kann, da die Zeilenreihenfolge der Unterabfrage in der Aggregatfunktion nicht unbedingt beibehalten wird.
Soll ich die Abfrage umschreiben?
quelle
(identifier)
der Primärschlüssel vonclass
?Antworten:
Ja. Aggregieren Sie direkt mit einem ARRAY-Konstruktor in der lateralen Unterabfrage:
Sie brauchen auch nicht
GROUP BY
in der äußeren aufSELECT
diese Weise. Kürzer, sauberer, schneller.Ich habe das
LEFT JOIN
durch eine Ebene ersetzt,CROSS JOIN
da der ARRAY-Konstruktor immer genau 1 Zeile zurückgibt. (Wie Sie in einem Kommentar betont haben.)db <> hier fummeln .
Verbunden:
Reihenfolge der Zeilen in Unterabfragen
So adressieren Sie Ihren Kommentar :
Nun, nein. Während der SQL-Standard keine Garantien bietet, gibt es in Postgres begrenzte Garantien . Das Handbuch:
Wenn Sie in der nächsten Ebene nur Zeilen aggregieren, ist die Reihenfolge positiv garantiert. Ja, was wir dem ARRAY-Konstruktor zuführen , ist auch eine Unterabfrage . Das ist nicht der Punkt. Es würde auch funktionieren mit
array_agg()
:Ich erwarte jedoch, dass der ARRAY-Konstruktor für den Fall schneller ist. Sehen:
quelle
ARRAY(...)
Konstruktion zugeführt werden ?SELECT ... FROM (SELECT ... FROM ...)
). Dies ist einSELECT
auf einemSELECT
:SELECT ARRAY(SELECT ... FROM ...)
.left lateral join
durch gerecht ersetzen könnenlateral join
? In Abwesenheit von Personen gibt die erste Abfrage ein leeres Array zurück, die zweitenull
, oder? Dies widerspricht dem letzten Satz von dba.stackexchange.com/questions/173831/… , aber ich denke, dass Informationen falsch sind? Ich denke, wir müssen eincheckenp.persons is not null
(bei der ersten Abfrage) oderp.persons != '{}'
(bei der zweiten Abfrage), um nur Klassen mit mindestens einer Person auszugeben.Hier ist eine Alternative, aber sie ist nicht besser als die, die Sie bereits haben:
quelle
enumeration
die vollständigeperson
Tabelle, wenn ich mich nicht irre (da CTEs eine Speicherbarriere in PostgreSQL darstellen). Es ist im Grunde eine In-Memory-Kopie derperson
Tabelle. Dies ist möglicherweise nicht ideal, da wir im Wesentlichen nur einige Zeilen aus dieser Tabelle benötigen (2 für jede Klasse, um genau zu sein). Vielleicht sollten wir einselect * from (...) where n <= 2
um die Abfrage herum hinzufügenenumeration
(anstatt in der Hauptabfrage)? Auf diese Weise enthält der CTEenumeration
nicht mehr die gesamteperson
Tabelle.