PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Cláusula ORDER BY ... USING no PostgreSQL


Um exemplo bem simples seria:
> SELECT * FROM tab ORDER BY col USING <

Mas isso é chato, porque isso não é nada que você não consiga com o tradicional ORDER BY col ASC .

Além disso, o catálogo padrão não menciona nada empolgante sobre funções/operadores de comparação estranhos. Você pode obter uma lista deles:
    > 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);

Você notará que existem principalmente < e > funções para tipos primitivos como integer , date etc e mais alguns para matrizes e vetores e assim por diante. Nenhum desses operadores o ajudará a obter um pedido personalizado.

Na maioria casos em que a ordenação personalizada é necessária, você pode usar algo como ... ORDER BY somefunc(tablecolumn) ... onde somefunc mapeia os valores adequadamente. Como isso funciona com todos os bancos de dados, essa também é a maneira mais comum. Para coisas simples, você pode até escrever uma expressão em vez de uma função personalizada.

Mudando de marcha

ORDER BY ... USING faz sentido em vários casos:
  • A ordenação é tão incomum que o somefunc truque não funciona.
  • Você trabalha com um tipo não primitivo (como point , circle ou números imaginários) e você não quer se repetir em suas consultas com cálculos estranhos.
  • O conjunto de dados que você deseja classificar é tão grande que o suporte por um índice é desejado ou até mesmo necessário.

Vou me concentrar nos tipos de dados complexos:geralmente há mais de uma maneira de classificá-los de maneira razoável. Um bom exemplo é point :Você pode "ordená-los" pela distância até (0,0) ou por x primeiro, depois por y ou apenas por y ou qualquer outra coisa que você queira.

É claro que o PostgreSQL tem operadores predefinidos para point :
    > CREATE TABLE p ( p point );
    > SELECT p <-> point(0,0) FROM p;

Mas nenhuma deles é declarado utilizável para ORDER BY por padrão (veja acima):
    > 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.

Operadores simples para point são os operadores "abaixo" e "acima" <^ e >^ . Eles comparam simplesmente o y parte do ponto. Mas:
    >  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 requer um operador com semântica definida:Obviamente deve ser um operador binário, deve aceitar o mesmo tipo de argumentos e deve retornar booleano. Eu acho que também deve ser transitivo (se a btree adequado -ordenação de índice. Isso explica as estranhas mensagens de erro contendo a referência a btree .

ORDER BY USING também requer não apenas um operador a ser definido, mas uma classe de operador e uma família de operadores . Enquanto um poderia implementar a ordenação com apenas um operador, o PostgreSQL tenta ordenar de forma eficiente e minimizar as comparações. Portanto, vários operadores são usados ​​mesmo quando você especifica apenas um - os outros devem seguir certas restrições matemáticas - já mencionei transitividade, mas há mais.

Mudando de marcha

Vamos definir algo adequado:Um operador para pontos que compara apenas o y papel.

O primeiro passo é criar uma família de operadores personalizada que pode ser usada pela btree método de acesso ao índice. Vejo
    > CREATE OPERATOR FAMILY xyzfam USING btree;   -- superuser access required!
    CREATE OPERATOR FAMILY

Em seguida, devemos fornecer uma função de comparação que retorna -1, 0, +1 ao comparar dois pontos. Esta função VOCÊ ser chamado internamente!
    > CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int 
      AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
    CREATE FUNCTION

Em seguida, definimos a classe de operadores para a família. Consulte o manual para obter uma explicação dos números.
    > 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

Esta etapa combina vários operadores e funções e também define sua relação e significado. Por exemplo OPERATOR 1 significa:Este é o operador para less-than testes.

Agora os operadores <^ e >^ pode ser usado em 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 - ordenado por y .

Resumindo: ORDER BY ... USING é uma visão interessante sob o capô do PostgreSQL. Mas nada que você vai precisar tão cedo, a menos que você trabalhe em muito áreas específicas da tecnologia de banco de dados.

Outro exemplo pode ser encontrado nos documentos do Postgres. com código-fonte para o exemplo aqui e aqui. Este exemplo também mostra como criar os operadores.