PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

ORDER BY ... USING-Klausel in PostgreSQL

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.