sql >> Databasteknik >  >> RDS >> PostgreSQL

BESTÄLL MED ... ANVÄNDA satsen i PostgreSQL

Ett mycket enkelt exempel skulle vara:

> SELECT * FROM tab ORDER BY col USING <

Men det här är tråkigt, för det här är inget du inte kan få med den traditionella ORDER BY col ASC .

Standardkatalogen nämner inte heller något spännande om konstiga jämförelsefunktioner/operatorer. Du kan få en lista över dem:

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

Du kommer att märka att det mestadels finns < och > funktioner för primitiva typer som heltal , datum etc och lite mer för arrayer och vektorer och så vidare. Ingen av dessa operatörer hjälper dig att få en anpassad beställning.

I de flesta fall där anpassad beställning krävs kan du komma undan med något som ... BESTÄLL MED somefunc(tabellkolumn) ... där somefunc kartlägger värdena på lämpligt sätt. Eftersom det fungerar med varje databas är detta också det vanligaste sättet. För enkla saker kan du till och med skriva ett uttryck istället för en anpassad funktion.

Växlar upp

BESTÄLL GENOM ... ANVÄNDA är vettigt i flera fall:

  • Beställningen är så ovanlig att somefunc tricket fungerar inte.
  • Du arbetar med en icke-primitiv typ (som point , cirkel eller imaginära siffror) och du vill inte upprepa dig själv i dina frågor med konstiga beräkningar.
  • Datauppsättningen du vill sortera är så stor att stöd av ett index önskas eller till och med krävs.

Jag kommer att fokusera på de komplexa datatyperna:ofta finns det mer än ett sätt att sortera dem på ett rimligt sätt. Ett bra exempel är point :Du kan "beställa" dem efter avståndet till (0,0), eller med x först, sedan av y eller bara av y eller något annat du vill.

PostgreSQL har såklart fördefinierade operatorer för punkt :

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

Men ingen av dem förklaras användbara för ORDER BY som standard (se ovan):

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

Enkla operatorer för point är operatorerna "under" och "ovan" <^ och >^ . De jämför helt enkelt y del av poängen. Men:

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

BESTÄLL GENOM kräver en operator med definierad semantik:Uppenbarligen måste den vara en binär operator, den måste acceptera samma typ som argument och den måste returnera boolesk. Jag tror att det också måste vara transitivt (om a btree -indexbeställning. Detta förklarar de konstiga felmeddelandena som innehåller referensen till btree .

BESTÄLL GENOM kräver inte bara en operatör ska definieras men en operatörsklass och en operatörsfamilj . Medan man kunde implementera sortering med endast en operatör, PostgreSQL försöker sortera effektivt och minimera jämförelser. Därför används flera operatorer även när du bara anger en - de andra måste följa vissa matematiska begränsningar - jag har redan nämnt transitivitet, men det finns fler.

Växlar upp

Låt oss definiera något lämpligt:​​En operator för punkter som endast jämför y del.

Det första steget är att skapa en anpassad operatörsfamilj som kan användas av btree indexåtkomstmetod. se

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

Därefter måste vi tillhandahålla en komparatorfunktion som returnerar -1, 0, +1 när vi jämför två punkter. Denna funktion KOMMER bli uppringd internt!

    > 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ärefter definierar vi operatörsklassen för familjen. Se manualen för en förklaring av siffrorna.

    > 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

Detta steg kombinerar flera operatorer och funktioner och definierar även deras relation och betydelse. Till exempel OPERATOR 1 betyder:Detta är operatorn för mindre än tester.

Nu är operatorerna <^ och >^ kan användas i 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 - sorterat efter y .

För att sammanfatta det: BESTÄLL GENOM ... ANVÄNDA är en intressant look under huven på PostgreSQL. Men inget du kommer att kräva när som helst snart om du inte arbetar i mycket specifika områden av databasteknik.

Ett annat exempel finns i Postgres docs. med källkod för exemplet här och här. Detta exempel visar också hur man skapar operatorerna.



  1. Hur du vårrensar din databas

  2. Testa för noll i funktion med varierande parametrar

  3. Talk slides:Partitioneringsförbättringar i PostgreSQL 11

  4. Finns det ett alternativ till TOP i MySQL?