PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

ORDER BY ... USING клауза в PostgreSQL

Много прост пример би бил:

> 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 или само от 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)

Voila - сортирано по y .

За да обобщим: ORDER BY ... USING е интересен поглед под капака на PostgreSQL. Но нищо, което няма да ви е необходимо скоро, освен ако не работите в много специфични области на технологията на базата данни.

Друг пример може да се намери в документите на Postgres. с изходен код за примера тук и тук. Този пример също показва как да създадете операторите.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. GroupingError:ERROR:колоната трябва да се появи в клаузата GROUP BY или да се използва в агрегатна функция

  2. fe_sendauth:не е предоставена парола

  3. Рекурсивна заявка на Postgres с row_to_json

  4. Премахване на дубликат от таблица

  5. Какво означава, когато процес на PostgreSQL е неактивен в транзакцията?