Un esempio molto semplice potrebbe essere:
> SELECT * FROM tab ORDER BY col USING <
Ma questo è noioso, perché non è niente che non puoi ottenere con il tradizionale ORDER BY col ASC
.
Anche il catalogo standard non menziona nulla di eccitante su strane funzioni/operatori di confronto. Puoi ottenerne un elenco:
> 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);
Noterai che ci sono principalmente <
e >
funzioni per tipi primitivi come integer
, date
ecc e altri ancora per array e vettori e così via. Nessuno di questi operatori ti aiuterà a ottenere un ordine personalizzato.
Nella maggior parte nei casi in cui è richiesto un ordine personalizzato puoi cavartela usando qualcosa come ... ORDER BY somefunc(tablecolumn) ...
dove somefunc
mappa i valori in modo appropriato. Poiché funziona con ogni database, questo è anche il modo più comune. Per cose semplici puoi persino scrivere un'espressione invece di una funzione personalizzata.
Cambio di marcia
ORDER BY ... USING
ha senso in diversi casi:
- L'ordine è così raro che
somefunc
il trucco non funziona. - Lavori con un tipo non primitivo (come
point
,circle
o numeri immaginari) e non vuoi ripeterti nelle tue domande con strani calcoli. - Il set di dati che vuoi ordinare è così grande che il supporto di un indice è desiderato o addirittura richiesto.
Mi concentrerò sui tipi di dati complessi:spesso c'è più di un modo per ordinarli in modo ragionevole. Un buon esempio è point
:puoi "ordinarli" in base alla distanza fino a (0,0) o in base a x prima, poi da y o semplicemente da y o qualsiasi altra cosa tu voglia.
Naturalmente, PostgreSQL ha operatori predefiniti per point
:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
Ma nessuno di essi è dichiarato utilizzabile per ORDER BY
per impostazione predefinita (vedi sopra):
> 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.
Operatori semplici per point
sono gli operatori "sotto" e "sopra" <^
e >^
. Confrontano semplicemente il y
parte del punto. Ma:
> 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
richiede un operatore con semantica definita:Ovviamente deve essere un operatore binario, deve accettare come argomenti lo stesso tipo e deve restituire booleano. Penso che debba essere anche transitivo (se a btree -ordinamento dell'indice. Questo spiega gli strani messaggi di errore che contengono il riferimento a btree .
ORDER BY USING
richiede inoltre non solo un operatore da definire ma una classe operatore e una famiglia di operatori . Mentre uno potrebbe implementa l'ordinamento con un solo operatore, PostgreSQL cerca di ordinare in modo efficiente e ridurre al minimo i confronti. Pertanto, vengono utilizzati più operatori anche quando ne specifichi uno solo - gli altri devono rispettare determinati vincoli matematici - ho già menzionato la transitività, ma ce ne sono di più.
Cambio di marcia
Definiamo qualcosa di adatto:un operatore per punti che confronta solo il y
parte.
Il primo passaggio consiste nel creare una famiglia di operatori personalizzata che può essere utilizzata da btree metodo di accesso all'indice. vedi
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
Successivamente dobbiamo fornire una funzione di confronto che restituisce -1, 0, +1 quando si confrontano due punti. Questa funzione SARA' essere chiamato internamente!
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
Quindi definiamo la classe dell'operatore per la famiglia. Vedere il manuale per una spiegazione dei numeri.
> 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
Questo passaggio combina diversi operatori e funzioni e ne definisce anche la relazione e il significato. Ad esempio OPERATOR 1
significa:Questo è l'operatore per less-than
test.
Ora gli operatori <^
e >^
può essere utilizzato in 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à - ordinato per y .
Per riassumere: ORDER BY ... USING
è uno sguardo interessante sotto il cofano di PostgreSQL. Ma nulla di cui avrai bisogno a breve a meno che tu non lavori in molto aree specifiche della tecnologia dei database.
Un altro esempio può essere trovato nei documenti di Postgres. con il codice sorgente per l'esempio qui e qui. Questo esempio mostra anche come creare gli operatori.