Очень простой пример:
> SELECT * FROM tab ORDER BY col USING <
Но это скучно, потому что вы ничего не можете получить с традиционным ORDER BY col ASC
.
Также в стандартном каталоге не упоминается ничего интересного о странных функциях / операторах сравнения. Вы можете получить их список:
> 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);
Вы заметите, что в основном существуют функции <
и >
для примитивных типов, таких как integer
, date
и т. Д., И некоторые другие для массивов и векторов и так далее. Ни один из этих операторов не поможет вам получить индивидуальный заказ.
В большинстве случаев, когда требуется пользовательский порядок, вы можете уйти, используя что-то вроде ... ORDER BY somefunc(tablecolumn) ...
, где somefunc
отображает значения соответствующим образом. Поскольку это работает с каждой базой данных, это также самый распространенный способ. Для простых вещей вы даже можете написать выражение вместо пользовательской функции.
Переключение вверх
ORDER BY ... USING
имеет смысл в нескольких случаях:
- Порядок настолько необычен, что трюк
somefunc
не работает.
- Вы работаете с не примитивным типом (например,
point
, circle
или воображаемыми числами) и не хотите повторять себя в своих запросах со странными вычислениями.
- Набор данных, который вы хотите отсортировать, настолько велик, что поддержка по индексу желательна или даже необходима.
Я остановлюсь на сложных типах данных: часто существует более одного способа их разумной сортировки. Хорошим примером является point
: вы можете «упорядочить» их по расстоянию до (0,0) или сначала по x , затем по y или просто по у или все, что вы хотите.
Конечно, PostgreSQL имеет предопределенных операторов для point
:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
Но нет из них объявлены пригодными для ORDER BY
по умолчанию (см. Выше):
> 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.
Простыми операторами для point
являются операторы «внизу» и «выше» <^
и >^
. Они сравнивают просто часть y
. Но:
> 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
требует оператора с определенной семантикой: очевидно, это должен быть двоичный оператор, он должен принимать тот же тип, что и аргументы, и он должен возвращать логическое значение. Я думаю, что он также должен быть транзитивным (если a btree -индексного заказа. Это объясняет странные сообщения об ошибках, содержащие ссылку на btree .
ORDER BY USING
также требует не только одного оператора , но и класса операторов и семейства операторов . В то время как один может реализовать сортировку только с одним оператором, PostgreSQL пытается эффективно сортировать и минимизировать сравнения. Поэтому несколько операторов используются, даже когда вы указываете только один - другие должны придерживаться определенных математических ограничений - я уже упоминал о транзитивности, но есть и другие.
Переключение передач
Давайте определим что-то подходящее: оператор для точек, который сравнивает только часть y
.
Первым шагом является создание пользовательского семейства операторов, которое можно использовать методом доступа к индексу btree . см
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
Далее мы должны предоставить функцию сравнения, которая возвращает -1, 0, +1 при сравнении двух точек. Эта функция БУДЕТ вызываться изнутри!
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
Далее мы определяем класс операторов для семейства. См. Руководство для объяснения чисел.
> 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
Этот шаг объединяет несколько операторов и функций, а также определяет их отношения и значение. Например, OPERATOR 1
означает: это оператор для less-than
испытаний.
Теперь операторы <^
и >^
могут использоваться в 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)
Вуаля - отсортировано по y .
Подводя итог: ORDER BY ... USING
- интересный взгляд под капотом PostgreSQL. Но ничего вам не потребуется в ближайшее время, если вы не будете работать в очень определенных областях технологии баз данных.
Другой пример можно найти в документации Postgres. с исходным кодом для примера здесь и здесь . В этом примере также показано, как создавать операторы.