PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Klauzula ORDER BY ... USING w PostgreSQL

Bardzo prostym przykładem byłoby:

> SELECT * FROM tab ORDER BY col USING <

Ale to jest nudne, ponieważ to nic, czego nie można uzyskać za pomocą tradycyjnego ORDER BY col ASC .

Również standardowy katalog nie wspomina nic ekscytującego o dziwnych funkcjach/operatorach porównania. Możesz otrzymać ich listę:

    > 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);

Zauważysz, że w większości są to < i > funkcje dla typów pierwotnych, takich jak integer , data itd. i jeszcze trochę dla tablic i wektorów i tak dalej. Żaden z tych operatorów nie pomoże Ci uzyskać niestandardowego zamówienia.

W większości przypadki, w których wymagane jest niestandardowe zamówienie, możesz uciec, używając czegoś takiego jak ... ORDER BY somefunc(tablecolumn) ... gdzie jakaśfunkcja odpowiednio mapuje wartości. Ponieważ działa to z każdą bazą danych, jest to również najczęstszy sposób. W przypadku prostych rzeczy możesz nawet napisać wyrażenie zamiast funkcji niestandardowej.

Zmiana biegów w górę

ZAMÓW PRZEZ... UŻYWAJĄC ma sens w kilku przypadkach:

  • Kolejność jest tak rzadka, że ​​jakaśfunc sztuczka nie działa.
  • Pracujesz z nieprymitywnym typem (takim jak point , kółko lub liczb urojonych) i nie chcesz powtarzać się w swoich zapytaniach z dziwnymi obliczeniami.
  • Zbiór danych, który chcesz posortować, jest tak duży, że wsparcie przez indeks jest pożądane, a nawet wymagane.

Skoncentruję się na złożonych typach danych:często istnieje więcej niż jeden sposób ich rozsądnego sortowania. Dobrym przykładem jest point :Możesz „uporządkować” je według odległości do (0,0) lub według x najpierw, potem przez y lub po prostu przez y lub cokolwiek innego, co chcesz.

Oczywiście PostgreSQL ma predefiniowane operatory dla punktu :

    > CREATE TABLE p ( p point );
    > SELECT p <-> point(0,0) FROM p;

Ale żadne z nich jest zadeklarowana do użycia dla ORDER BY domyślnie (patrz wyżej):

    > 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.

Proste operatory dla punktu to operatory „poniżej” i „powyżej” <^ i >^ . Porównują po prostu y część punktu. 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.

ZAMÓW PRZY UŻYCIU wymaga operatora ze zdefiniowaną semantyką:Oczywiście musi to być operator binarny, musi akceptować ten sam typ co argumenty i musi zwracać wartość logiczną. Myślę, że musi być również przechodnia (jeśli a btree -indeks porządkowania. To wyjaśnia dziwne komunikaty o błędach zawierające odniesienie do btree .

ZAMÓW PRZY UŻYCIU wymaga również nie tylko jednego operatora do zdefiniowania, ale klasa operatora i rodzina operatorów . Chociaż jeden może zaimplementuj sortowanie za pomocą tylko jednego operatora, PostgreSQL stara się efektywnie sortować i minimalizować porównania. Dlatego używa się kilku operatorów, nawet jeśli określisz tylko jeden – pozostałe muszą być zgodne z pewnymi ograniczeniami matematycznymi – wspomniałem już o przechodniości, ale jest ich więcej.

Przełączanie biegów w górę

Zdefiniujmy coś odpowiedniego:operator punktów, który porównuje tylko y część.

Pierwszym krokiem jest utworzenie niestandardowej rodziny operatorów, która może być używana przez btree metoda dostępu do indeksu. patrz

    > CREATE OPERATOR FAMILY xyzfam USING btree;   -- superuser access required!
    CREATE OPERATOR FAMILY

Następnie musimy podać funkcję porównawczą, która zwraca -1, 0, +1 przy porównywaniu dwóch punktów. Ta funkcja BĘDZIE nazywać się wewnętrznie!

    > CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int 
      AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
    CREATE FUNCTION

Następnie definiujemy klasę operatora dla rodziny. Zapoznaj się z instrukcją obsługi, aby uzyskać objaśnienie liczb.

    > 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

Ten krok łączy kilka operatorów i funkcji, a także określa ich związek i znaczenie. Na przykład OPERATOR 1 oznacza:To jest operator dla mniejsze-niż testy.

Teraz operatory <^ i >^ może być używany w ZAMÓW PRZEZ UŻYCIE :

> 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 – posortowane według y .

Podsumowując: ZAMÓW PRZEZ... UŻYWAJĄC to ciekawe spojrzenie pod maską PostgreSQL. Ale niczego nie będziesz potrzebować w najbliższym czasie, chyba że będziesz pracować w bardzo określone obszary technologii baz danych.

Inny przykład można znaleźć w dokumentacji Postgres. z kodem źródłowym dla przykładu tutaj i tutaj. Ten przykład pokazuje również, jak tworzyć operatory.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tworzenie częściowego unikalnego indeksu za pomocą sqlalchemy na Postgres

  2. CS50:operator LIKE, podstawianie zmiennych z rozszerzeniem %

  3. Sortuj wartości NULL na końcu tabeli

  4. Jak stworzyć indeks dla elementów tablicy w PostgreSQL?

  5. Jak tworzyć dynamiczne deklaracje postgresowe w PHP