Ein sehr einfaches Beispiel wäre:
> SELECT * FROM tab ORDER BY col USING <
Aber das ist langweilig, denn das ist nichts, was Sie mit dem traditionellen ORDER BY col ASC
nicht bekommen könnten .
Auch der Standardkatalog erwähnt nichts Spannendes über seltsame Vergleichsfunktionen/Operatoren. Sie können eine Liste davon erhalten:
> SELECT amoplefttype::regtype, amoprighttype::regtype, amopopr::regoper
FROM pg_am JOIN pg_amop ON pg_am.oid = pg_amop.amopmethod
WHERE amname = 'btree' AND amopstrategy IN (1,5);
Sie werden feststellen, dass es sich hauptsächlich um <
handelt und >
Funktionen für primitive Typen wie integer
, date
etc und einige mehr für Arrays und Vektoren und so weiter. Keiner dieser Operatoren wird Ihnen helfen, eine benutzerdefinierte Bestellung zu erhalten.
In den meisten In Fällen, in denen eine benutzerdefinierte Reihenfolge erforderlich ist, können Sie etwas wie ... ORDER BY somefunc(tablecolumn) ...
verwenden wo somefunc
ordnet die Werte entsprechend zu. Da das mit jeder Datenbank funktioniert, ist dies auch der gebräuchlichste Weg. Für einfache Dinge können Sie sogar einen Ausdruck anstelle einer benutzerdefinierten Funktion schreiben.
Hochschalten
ORDER BY ... USING
macht in einigen Fällen Sinn:
- Die Reihenfolge ist so ungewöhnlich, dass
somefunc
Trick funktioniert nicht. - Sie arbeiten mit einem nicht primitiven Typ (wie
point
,circle
oder imaginäre Zahlen) und Sie sich in Ihren Abfragen nicht mit seltsamen Berechnungen wiederholen möchten. - Der zu sortierende Datensatz ist so groß, dass eine Unterstützung durch einen Index gewünscht oder sogar erforderlich ist.
Ich werde mich auf die komplexen Datentypen konzentrieren:Oft gibt es mehr als eine Möglichkeit, sie sinnvoll zu sortieren. Ein gutes Beispiel ist point
:Sie können sie nach der Entfernung zu (0,0) oder nach x "ordnen". zuerst, dann von y oder einfach per y oder alles andere, was Sie wollen.
Natürlich hat PostgreSQL vordefinierte Operatoren für point
:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
Aber keine von ihnen wird für ORDER BY
als verwendbar erklärt standardmäßig (siehe oben):
> SELECT * FROM p ORDER BY p;
ERROR: could not identify an ordering operator for type point
TIP: Use an explicit ordering operator or modify the query.
Einfache Operatoren für point
sind die Operatoren "unten" und "oben" <^
und >^
. Sie vergleichen einfach das y
Teil des Punktes. Aber:
> SELECT * FROM p ORDER BY p USING >^;
ERROR: operator > is not a valid ordering operator
TIP: Ordering operators must be "<" or ">" members of __btree__ operator families.
ORDER BY USING
erfordert einen Operator mit definierter Semantik:Offensichtlich muss es ein binärer Operator sein, er muss denselben Typ wie Argumente akzeptieren und einen booleschen Wert zurückgeben. Ich denke, es muss auch transitiv sein (wenn a btree notwendig -Indexordnung. Das erklärt die merkwürdigen Fehlermeldungen mit dem Verweis auf btree .
ORDER BY USING
erfordert auch nicht nur einen Bediener zu definieren, sondern eine Operatorklasse und eine Bedienerfamilie . Während man konnte Implementieren Sie die Sortierung mit nur einem Operator, PostgreSQL versucht, effizient zu sortieren und Vergleiche zu minimieren. Daher werden mehrere Operatoren verwendet, auch wenn Sie nur einen angeben - die anderen müssen bestimmte mathematische Einschränkungen einhalten - ich habe bereits die Transitivität erwähnt, aber es gibt noch mehr.
Hochschalten
Definieren wir etwas Passendes:Einen Operator für Punkte, der nur das y
vergleicht Teil.
Der erste Schritt besteht darin, eine benutzerdefinierte Operatorfamilie zu erstellen, die vom btree verwendet werden kann Index-Zugriffsmethode. siehe
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
Als nächstes müssen wir eine Komparatorfunktion bereitstellen, die -1, 0, +1 zurückgibt, wenn zwei Punkte verglichen werden. Diese Funktion WILL intern angerufen werden!
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
Als nächstes definieren wir die Operatorklasse für die Familie. Eine Erläuterung der Nummern finden Sie im Handbuch.
> CREATE OPERATOR CLASS xyz_ops FOR TYPE point USING btree FAMILY xyzfam AS
OPERATOR 1 <^ ,
OPERATOR 3 ?- ,
OPERATOR 5 >^ ,
FUNCTION 1 xyz_v_cmp(point, point) ;
CREATE OPERATOR CLASS
Dieser Schritt kombiniert mehrere Operatoren und Funktionen und definiert auch ihre Beziehung und Bedeutung. Zum Beispiel OPERATOR 1
bedeutet:Das ist der Operator für less-than
Tests.
Nun die Operatoren <^
und >^
kann in ORDER BY USING
verwendet werden :
> INSERT INTO p SELECT point(floor(random()*100), floor(random()*100)) FROM generate_series(1, 5);
INSERT 0 5
> SELECT * FROM p ORDER BY p USING >^;
p
---------
(17,8)
(74,57)
(59,65)
(0,87)
(58,91)
Voila - sortiert nach y .
Um es zusammenzufassen: ORDER BY ... USING
ist ein interessanter Blick unter die Haube von PostgreSQL. Aber nichts, was Sie in absehbarer Zeit benötigen werden, es sei denn, Sie arbeiten sehr bestimmte Bereiche der Datenbanktechnologie.
Ein weiteres Beispiel finden Sie in der Postgres-Dokumentation. mit Quellcode für das Beispiel hier und hier. Dieses Beispiel zeigt auch, wie die Operatoren erstellt werden.