sql >> Base de Datos >  >> RDS >> PostgreSQL

Cláusula ORDER BY ... USING en PostgreSQL

Un ejemplo muy simple sería:

> SELECT * FROM tab ORDER BY col USING <

Pero esto es aburrido, porque no es nada que no puedas obtener con el tradicional ORDER BY col ASC .

Además, el catálogo estándar no menciona nada interesante sobre funciones/operadores de comparación extraños. Puede obtener una lista de ellos:

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

Notarás que en su mayoría hay < y > funciones para tipos primitivos como integer , date etc. y algunos más para matrices y vectores, etc. Ninguno de estos operadores lo ayudará a obtener un pedido personalizado.

En la mayoría casos en los que se requiere un pedido personalizado, puede salirse con la suya usando algo como ... ORDER BY somefunc(tablecolumn) ... donde somefunc mapea los valores apropiadamente. Debido a que eso funciona con todas las bases de datos, esta es también la forma más común. Para cosas simples, incluso puede escribir una expresión en lugar de una función personalizada.

Cambiando de marcha

ORDER BY ... USING tiene sentido en varios casos:

  • El orden es tan poco común que somefunc el truco no funciona.
  • Trabajas con un tipo no primitivo (como point , circle o números imaginarios) y no querrás repetirte en tus consultas con cálculos extraños.
  • El conjunto de datos que desea clasificar es tan grande que se desea o incluso se requiere la compatibilidad con un índice.

Me centraré en los tipos de datos complejos:a menudo hay más de una forma de clasificarlos de forma razonable. Un buen ejemplo es point :Puedes "ordenarlos" por la distancia a (0,0), o por x primero, luego por y o simplemente por y o cualquier otra cosa que quieras.

Por supuesto, PostgreSQL tiene operadores predefinidos para point :

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

Pero ninguno de ellos se declara utilizable para ORDER BY por defecto (ver arriba):

    > 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 son los operadores "abajo" y "arriba" <^ y >^ . Comparan simplemente el y parte del punto. Pero:

    >  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 requiere un operador con semántica definida:Obviamente debe ser un operador binario, debe aceptar el mismo tipo como argumentos y debe devolver booleano. Creo que también debe ser transitivo (si a btree adecuado. -Ordenamiento de índices. Esto explica los extraños mensajes de error que contienen la referencia a btree .

ORDER BY USING también requiere no solo un operador por definir pero una clase de operador y una familia de operadores . Mientras que uno podría implementar la ordenación con un solo operador, PostgreSQL intenta ordenar de manera eficiente y minimizar las comparaciones. Por lo tanto, se utilizan varios operadores incluso cuando especifica solo uno; los demás deben cumplir ciertas restricciones matemáticas; ya mencioné la transitividad, pero hay más.

Cambiando de velocidad

Definamos algo adecuado:Un operador para puntos que compara solo el y parte.

El primer paso es crear una familia de operadores personalizados que pueda usar el btree método de acceso al índice. ver

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

A continuación, debemos proporcionar una función de comparación que devuelva -1, 0, +1 al comparar dos puntos. Esta función SERA ser llamado 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

A continuación, definimos la clase de operador para la familia. Consulte el manual para obtener una explicación de los 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

Este paso combina varios operadores y funciones y también define su relación y significado. Por ejemplo OPERATOR 1 significa:Este es el operador para less-than pruebas.

Ahora los operadores <^ y >^ se puede usar en 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 - ordenados por y .

Para resumir: ORDER BY ... USING es una mirada interesante bajo el capó de PostgreSQL. Pero nada de lo que necesitará en el corto plazo a menos que trabaje en muy áreas específicas de la tecnología de bases de datos.

Puede encontrar otro ejemplo en los documentos de Postgres. con código fuente para el ejemplo aquí y aquí. Este ejemplo también muestra cómo crear los operadores.