Die ORDER BY-Klausel wird in der PostgreSQL-Dokumentation beschrieben als:
ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]
Kann mir jemand Beispiele geben, wie man den USING operator
verwendet? Kann eine abwechselnde Reihenfolge der Ergebnismenge abgerufen werden?
Ein sehr einfaches Beispiel wäre:
> SELECT * FROM tab ORDER BY col USING <
Aber das ist langweilig, denn das ist nichts, was man mit dem traditionellen ORDER BY col ASC
nicht bekommen kann.
Auch der Standardkatalog erwähnt nichts Spannendes an merkwürdigen Vergleichsfunktionen/Operatoren. Sie können eine Liste von ihnen 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 bemerken, dass es meistens <
und >
Funktionen für primitive Typen wie integer
, date
usw. und einige mehr für Arrays und Vektoren und so weiter gibt. Keiner dieser Operatoren hilft Ihnen dabei, eine benutzerdefinierte Bestellung zu erhalten.
In den meisten Fällen , in denen eine benutzerdefinierte Bestellung erforderlich ist, können Sie beispielsweise ... ORDER BY somefunc(tablecolumn) ...
verwenden, wobei somefunc
die Werte entsprechend abbildet. Da dies 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.
Gänge hochschalten
ORDER BY ... USING
ist in mehreren Fällen sinnvoll:
somefunc
nicht funktioniert.point
, circle
oder imaginären Zahlen) und möchten sich in Ihren Abfragen nicht mit seltsamen Berechnungen wiederholen.Ich werde mich auf die komplexen Datentypen konzentrieren: Oft gibt es mehr als eine Möglichkeit, sie auf vernünftige Weise zu sortieren. Ein gutes Beispiel ist point
: Sie können sie nach dem Abstand zu (0,0) "ordnen" oder nach x zuerst, dann nach y oder einfach durch y oder irgendetwas anderes, 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 ist standardmäßig für ORDER BY
verwendbar (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 <^
und >^
"unter" und "über". Sie vergleichen einfach den 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 sich um einen binären Operator handeln, er muss den gleichen Typ wie Argumente akzeptieren und einen Booleschen Wert zurückgeben. Ich denke, es muss auch transitiv sein (wenn a <b und b <c dann a <c). Möglicherweise gibt es weitere Anforderungen. Alle diese Anforderungen sind jedoch auch für eine ordnungsgemäße Btree - Indexreihenfolge erforderlich. Dies erklärt die seltsamen Fehlermeldungen, die den Verweis auf btree enthalten.
ORDER BY USING
erfordert auch, dass nicht nur ein Operator definiert wird, sondern eine Operator-Klasse und eine Betreiberfamilie . Während ein mit nur einem Operator sortieren konnte , versucht PostgreSQL, 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 -, die Transitivität habe ich bereits erwähnt, aber es gibt noch weitere.
Gang hochschalten
Definieren wir etwas Passendes: Einen Operator für Punkte, der nur den Teil y
vergleicht.
Der erste Schritt besteht darin, eine benutzerdefinierte Operatorfamilie zu erstellen, die von der Indexzugriffsmethode btree verwendet werden kann. 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 WIRD intern aufgerufen!
> 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 Operator-Klasse für die Familie. Siehe Handbuch zur Erläuterung der Nummern.
> 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 deren Beziehung und Bedeutung. Zum Beispiel bedeutet OPERATOR 1
: Dies ist der Operator für less-than
Tests.
Jetzt können die Operatoren <^
und >^
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, es sei denn, Sie arbeiten in sehr spezifischen Bereichen der Datenbanktechnologie.
Ein weiteres Beispiel ist in den Postgres-Dokumenten mit dem Quellcode für das Beispiel hier und hier . Dieses Beispiel zeigt auch, wie die Operatoren erstellt werden.
Proben:
CREATE TABLE test
(
id serial NOT NULL,
"number" integer,
CONSTRAINT test_pkey PRIMARY KEY (id)
)
insert into test("number") values (1),(2),(3),(0),(-1);
select * from test order by number USING > //gives 3=>2=>1=>0=>-1
select * from test order by number USING < //gives -1=>0=>1=>2=>3
Es entspricht also desc
und asc
. Sie können jedoch Ihren eigenen Operator verwenden. Dies ist das wesentliche Merkmal von USING
.
Schöne Antworten, aber sie haben keinen wirklich wertvollen Fall für USING erwähnt.
Wenn Sie einen Index mit einer nicht standardmäßigen Operatorenfamilie erstellen, z. B. varchar_pattern_ops (~> ~, ~ <~, ~> = ~ ...) anstelle von <,>,> = ... dann, wenn Sie basierend auf Index und Ihnen suchen Wenn Sie den Index in der richtigen Reihenfolge verwenden möchten, müssen Sie USING mit dem entsprechenden Operator angeben.
Dies kann mit einem solchen Beispiel veranschaulicht werden:
CREATE INDEX index_words_Word ON words(Word text_pattern_ops);
Vergleichen wir diese beiden Abfragen:
SELECT * FROM words WHERE Word LIKE 'o%' LIMIT 10;
und
SELECT * FROM words WHERE Word LIKE 'o%' ORDER BY Word LIMIT 10;
Der Unterschied zwischen ihren Ausführungen beträgt bei 500.000 DB-Wörtern fast 100-mal! Außerdem sind die Ergebnisse möglicherweise nicht korrekt in Nicht-C-Locale.
Wie könnte das passieren?
Wenn Sie eine Suche mit der Klausel LIKE und ORDER BY durchführen, führen Sie tatsächlich diesen Aufruf aus:
SELECT * FROM words WHERE Word ~>=~ 'o' AND Word ~<~'p' ORDER BY Word USING < LIMIT 10;
Ihr Index wurde mit dem Operator ~ <~ erstellt, daher kann PG den angegebenen Index nicht in einer angegebenen ORDER BY-Klausel verwenden. Um die Dinge richtig zu machen, muss die Abfrage in dieses Formular geschrieben werden:
SELECT * FROM words WHERE Word ~>=~ 'o' AND Word ~<~'p' ORDER BY Word USING ~<~ LIMIT 10;
oder
SELECT * FROM words WHERE Word LIKE 'o%' ORDER BY Word USING ~<~ LIMIT 10;
Optional kann man das Schlüsselwort ASC (aufsteigend) oder DESC .__ hinzufügen. (absteigend) nach jedem Ausdruck in der ORDER BY-Klausel. Wenn nicht angegeben, wird standardmäßig ASC verwendet. Alternativ kann ein bestimmtes Der Name des Bestelloperators kann in der USING-Klausel angegeben werden. Ein Der Besteller muss ein kleineres oder größeres Mitglied einiger .__ sein. B-Tree-Betreiberfamilie. ASC entspricht normalerweise USING <und DESC ist in der Regel gleichbedeutend mit USING>.
Es könnte ungefähr so aussehen, denke ich (ich habe keine Postgres, die das jetzt überprüfen können, aber ich werde es später überprüfen)
SELECT Name FROM Person
ORDER BY NameId USING >