Un exemple très simple serait :
> SELECT * FROM tab ORDER BY col USING <
Mais c'est ennuyeux, car ce n'est rien que vous ne puissiez obtenir avec le traditionnel ORDER BY col ASC
.
De plus, le catalogue standard ne mentionne rien d'excitant sur les fonctions/opérateurs de comparaison étranges. Vous pouvez en obtenir une liste :
> 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);
Vous remarquerez qu'il y a surtout <
et >
fonctions pour les types primitifs comme integer
, date
etc et encore plus pour les tableaux et les vecteurs et ainsi de suite. Aucun de ces opérateurs ne vous aidera à obtenir une commande personnalisée.
Dans la plupart les cas où une commande personnalisée est requise, vous pouvez vous en sortir en utilisant quelque chose comme ... ORDER BY somefunc(tablecolumn) ...
où somefunc
mappe les valeurs de manière appropriée. Parce que cela fonctionne avec toutes les bases de données, c'est aussi le moyen le plus courant. Pour des choses simples, vous pouvez même écrire une expression au lieu d'une fonction personnalisée.
Changer de vitesse
ORDER BY ... USING
a du sens dans plusieurs cas :
- La commande est si rare que le
somefunc
l'astuce ne marche pas. - Vous travaillez avec un type non primitif (comme
point
,circle
ou des nombres imaginaires) et vous ne voulez pas vous répéter dans vos requêtes avec des calculs étranges. - L'ensemble de données que vous souhaitez trier est si volumineux que la prise en charge par un index est souhaitée, voire requise.
Je vais me concentrer sur les types de données complexes :il existe souvent plusieurs façons de les trier de manière raisonnable. Un bon exemple est point
:Vous pouvez les "ordonner" par la distance à (0,0), ou par x d'abord, puis par y ou juste par y ou tout ce que vous voulez.
Bien sûr, PostgreSQL a opérateurs prédéfinis pour point
:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
Mais aucun d'entre eux est déclaré utilisable pour ORDER BY
par défaut (voir ci-dessus) :
> 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.
Opérateurs simples pour point
sont les opérateurs "inférieur" et "au-dessus" <^
et >^
. Ils comparent simplement les y
partie de la pointe. Mais :
> 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
nécessite un opérateur avec une sémantique définie :évidemment, il doit s'agir d'un opérateur binaire, il doit accepter le même type d'arguments et il doit renvoyer un booléen. Je pense qu'il doit aussi être transitif (si a btree -ordre d'index. Ceci explique les messages d'erreur étranges contenant la référence à btree .
ORDER BY USING
ne nécessite pas seulement un seul opérateur à définir mais une classe d'opérateur et une famille d'opérateurs . Alors qu'on pourrait mettre en œuvre le tri avec un seul opérateur, PostgreSQL essaie de trier efficacement et de minimiser les comparaisons. Par conséquent, plusieurs opérateurs sont utilisés même lorsque vous n'en spécifiez qu'un - les autres doivent respecter certaines contraintes mathématiques - j'ai déjà mentionné la transitivité, mais il y en a d'autres.
Changer de vitesse
Définissons quelque chose d'approprié :un opérateur pour les points qui ne compare que le y
partie.
La première étape consiste à créer une famille d'opérateurs personnalisée qui peut être utilisée par le btree méthode d'accès à l'index. voir
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
Ensuite, nous devons fournir une fonction de comparaison qui renvoie -1, 0, +1 lors de la comparaison de deux points. Cette fonction VA être appelé en interne !
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
Ensuite, nous définissons la classe d'opérateurs pour la famille. Voir le manuel pour une explication des nombres.
> 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
Cette étape combine plusieurs opérateurs et fonctions et définit également leur relation et leur signification. Par exemple OPERATOR 1
signifie :il s'agit de l'opérateur pour less-than
tests.
Maintenant les opérateurs <^
et >^
peut être utilisé dans ORDER BY USING
:
> 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)
Voilà - trié par y .
Pour résumer : ORDER BY ... USING
est un regard intéressant sous le capot de PostgreSQL. Mais rien dont vous aurez besoin de sitôt à moins que vous ne travailliez dans très domaines spécifiques de la technologie des bases de données.
Un autre exemple peut être trouvé dans la documentation Postgres. avec le code source pour l'exemple ici et ici. Cet exemple montre également comment créer les opérateurs.