sql >> Database >  >> RDS >> PostgreSQL

ORDER BY ... USING-clausule in PostgreSQL

Een heel eenvoudig voorbeeld zou zijn:

> SELECT * FROM tab ORDER BY col USING <

Maar dit is saai, want dit is niets wat je niet kunt krijgen met de traditionele ORDER BY col ASC .

Ook de standaardcatalogus vermeldt niets spannends over vreemde vergelijkingsfuncties/operators. Je kunt er een lijst van krijgen:

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

U zult merken dat er meestal < . zijn en > functies voor primitieve typen zoals integer , date enz. en nog wat meer voor arrays en vectoren enzovoort. Geen van deze operators zal u helpen om een ​​aangepaste bestelling te krijgen.

In de meeste gevallen waar aangepaste volgorde vereist is, kunt u wegkomen met iets als ... ORDER BY somefunc(tablecolumn) ... waar somefunc brengt de waarden op de juiste manier in kaart. Omdat dat bij elke database werkt is dit ook de meest gebruikelijke manier. Voor simpele dingen kun je zelfs een uitdrukking schrijven in plaats van een aangepaste functie.

Een versnelling hoger schakelen

ORDER BY ... USING is in verschillende gevallen zinvol:

  • De volgorde is zo ongewoon, dat de somefunc truc werkt niet.
  • Je werkt met een niet-primitief type (zoals point , circle of denkbeeldige getallen) en je wilt jezelf niet herhalen in je vragen met vreemde berekeningen.
  • De dataset die u wilt sorteren is zo groot, dat ondersteuning door een index gewenst of zelfs vereist is.

Ik zal me concentreren op de complexe datatypes:vaak is er meer dan één manier om ze op een redelijke manier te sorteren. Een goed voorbeeld is point :U kunt ze "bestellen" op afstand tot (0,0), of op x eerst, dan door y of gewoon door y of wat je maar wilt.

PostgreSQL heeft natuurlijk vooraf gedefinieerde operators voor point :

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

Maar geen daarvan is bruikbaar verklaard voor ORDER BY standaard (zie hierboven):

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

Eenvoudige operatoren voor point zijn de "below" en "above" operators <^ en >^ . Ze vergelijken eenvoudig de y deel van het punt. Maar:

    >  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 vereist een operator met gedefinieerde semantiek:het moet duidelijk een binaire operator zijn, het moet hetzelfde type accepteren als argumenten en het moet boolean retourneren. Ik denk dat het ook transitief moet zijn (als a btree -indexering bestellen. Dit verklaart de vreemde foutmeldingen met de verwijzing naar btree .

ORDER BY USING vereist ook niet slechts één operator te definiëren, maar een operatorklasse en een operatorfamilie . Terwijl men kon sortering met slechts één operator implementeert, probeert PostgreSQL efficiënt te sorteren en vergelijkingen te minimaliseren. Daarom worden er verschillende operatoren gebruikt, zelfs als je er maar één specificeert - de andere moeten aan bepaalde wiskundige beperkingen voldoen - ik heb transitiviteit al genoemd, maar er zijn er meer.

Overschakelen naar een hogere versnelling

Laten we iets geschikts definiëren:een operator voor punten die alleen de y . vergelijkt onderdeel.

De eerste stap is het maken van een aangepaste operatorfamilie die kan worden gebruikt door de btree index toegangsmethode. zie

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

Vervolgens moeten we een comparatorfunctie leveren die -1, 0, +1 retourneert bij het vergelijken van twee punten. Deze functie ZAL intern gebeld worden!

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

Vervolgens definiëren we de operatorklasse voor de familie. Zie de handleiding voor uitleg van de cijfers.

    > 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

Deze stap combineert verschillende operatoren en functies en definieert ook hun relatie en betekenis. Bijvoorbeeld OPERATOR 1 betekent:Dit is de operator voor less-than testen.

Nu de operators <^ en >^ kan worden gebruikt 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)

Voila - gesorteerd op y .

Om het samen te vatten: ORDER BY ... USING is een interessante blik onder de motorkap van PostgreSQL. Maar niets dat u snel nodig zult hebben, tenzij u in zeer . werkt specifieke gebieden van databasetechnologie.

Een ander voorbeeld is te vinden in de documenten van Postgres. met broncode voor het voorbeeld hier en hier. Dit voorbeeld laat ook zien hoe de operators gemaakt kunnen worden.



  1. Laravel 5.2 - Gebruik een string als aangepaste primaire sleutel voor welsprekende tabel wordt 0

  2. Oracle-uitvoerparameters retourneren vanuit een opgeslagen procedure in .NET

  3. Varchar-veld numeriek sorteren in MySQL

  4. Oracle SQL - REGEXP_LIKE bevat andere tekens dan a-z of A-Z