Velmi jednoduchý příklad by byl:
> SELECT * FROM tab ORDER BY col USING <
Ale to je nuda, protože to není nic, co byste nemohli získat tradičním ORDER BY col ASC
.
Také standardní katalog nezmiňuje nic vzrušujícího o podivných srovnávacích funkcích/operátorech. Jejich seznam můžete získat:
> 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);
Všimnete si, že většinou existují <
a >
funkce pro primitivní typy jako integer
, date
atd a některé další pro pole a vektory a tak dále. Žádný z těchto operátorů vám nepomůže získat vlastní objednávku.
Ve většině v případech, kdy je vyžadováno vlastní řazení, můžete se dostat pryč pomocí něčeho jako ... ORDER BY somefunc(tablecolumn) ...
kde somefunc
vhodně mapuje hodnoty. Protože to funguje s každou databází, je to také nejběžnější způsob. Pro jednoduché věci můžete dokonce napsat výraz místo vlastní funkce.
Přepnutí převodů nahoru
ORDER BY ... USING
dává smysl v několika případech:
- Řazení je tak neobvyklé, že
somefunc
trik nefunguje. - Pracujete s neprimitivním typem (jako
point
,circle
nebo imaginární čísla) a nechcete se ve svých dotazech opakovat s podivnými výpočty. - Soubor dat, který chcete seřadit, je tak velký, že je žádoucí nebo dokonce vyžadována podpora pomocí indexu.
Zaměřím se na komplexní datové typy:často existuje více než jeden způsob, jak je rozumným způsobem třídit. Dobrým příkladem je point
:Můžete je „uspořádat“ podle vzdálenosti do (0,0) nebo podle x nejprve a poté y nebo jen y nebo cokoliv jiného chcete.
PostgreSQL samozřejmě má předdefinované operátory pro point
:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
Ale žádné z nich je prohlášeno za použitelné pro ORDER BY
ve výchozím nastavení (viz výše):
> 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.
Jednoduché operátory pro point
jsou operátory „dole“ a „nad“ <^
a >^
. Porovnávají jednoduše y
část bodu. Ale:
> 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
vyžaduje operátor s definovanou sémantikou:Samozřejmě to musí být binární operátor, musí akceptovat stejný typ jako argumenty a musí vracet boolean. Myslím, že musí být také tranzitivní (pokud a btree - indexové řazení. To vysvětluje podivné chybové zprávy obsahující odkaz na btree .
ORDER BY USING
také vyžaduje nejen jednoho operátora být definován, ale třída operátorů a rodina operátorů . Zatímco jeden mohl implementovat třídění pouze s jedním operátorem, PostgreSQL se snaží třídit efektivně a minimalizovat porovnávání. Proto se používá několik operátorů, i když zadáte pouze jeden - ostatní musí dodržovat určitá matematická omezení - tranzitivitu jsem již zmínil, ale je jich více.
Přepnutí převodového stupně nahoru
Definujme něco vhodného:Operátor pro body, který porovnává pouze y
část.
Prvním krokem je vytvoření vlastní rodiny operátorů, kterou může bstrom používat metoda přístupu k indexu. viz
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
Dále musíme poskytnout funkci komparátoru, která vrátí -1, 0, +1 při porovnávání dvou bodů. Tato funkce BUDE být volán interně!
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
Dále definujeme třídu operátorů pro rodinu. Vysvětlení čísel naleznete v příručce.
> 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
Tento krok kombinuje několik operátorů a funkcí a také definuje jejich vztah a význam. Například OPERATOR 1
znamená:Toto je operátor pro less-than
testy.
Nyní operátory <^
a >^
lze použít v 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)
Voila – seřazeno podle y .
Abych to shrnul: ORDER BY ... USING
je zajímavý pohled pod pokličku PostgreSQL. Ale nic, co byste v dohledné době potřebovali, pokud nebudete pracovat velmi specifické oblasti databázové technologie.
Další příklad lze nalézt v dokumentu Postgres. se zdrojovým kódem pro příklad zde a zde. Tento příklad také ukazuje, jak vytvořit operátory.