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

Seleccionar múltiples valores max() usando una sola instrucción SQL

Una vez más, para algo más que unos pocos "tipos de datos", sugiero usar crosstab() :

SELECT * FROM crosstab(
     $$SELECT DISTINCT ON (1, 2)
              'max' AS "type", data_type, val
       FROM   tbl
       ORDER  BY 1, 2, val DESC$$

    ,$$VALUES ('Final Fantasy'), ('Quake 3'), ('World of Warcraft')$$)
AS x ("type" text, "Final Fantasy" int, "Quake 3" int, "World of Warcraft" int)

Devoluciones:

type | Final Fantasy | Quake 3 | World of Warcraft
-----+---------------+---------+-------------------
max  | 500           | 1500    |    1200

Más explicación de los conceptos básicos:
Consulta de tabulación cruzada de PostgreSQL

Solución dinámica

Lo complicado es hacer esto completamente dinámico :para que funcione para

  • un número desconocido de columnas (tipos_de_datos en este caso)
  • con nombres desconocidos (tipos_de_datos de nuevo)

Al menos el tipo es bien conocido:integer en este caso.

En resumen:eso no es posible con PostgreSQL actual (incluido 9.3). Hay aproximaciones con tipos polimórficos y formas de eludir las restricciones con matrices o tipos hstore. Puede ser lo suficientemente bueno para ti. Pero es estrictamente imposible para obtener el resultado con columnas individuales en una sola consulta SQL. SQL es muy rígido con respecto a los tipos y quiere saber qué esperar.

Sin embargo , se puede hacer con dos consultas El primero construye la consulta real para usar. Sobre la base del caso simple anterior:

SELECT $f$SELECT * FROM crosstab(
     $$SELECT DISTINCT ON (1, 2)
              'max' AS "type", data_type, val
       FROM   tbl
       ORDER  BY 1, 2, val DESC$$

    ,$$VALUES ($f$     || string_agg(quote_literal(data_type), '), (') || $f$)$$)
AS x ("type" text, $f$ || string_agg(quote_ident(data_type), ' int, ') || ' int)'
FROM  (SELECT DISTINCT data_type FROM tbl) x

Esto genera la consulta que realmente necesita. Ejecute el segundo dentro de la misma transacción para evitar problemas de concurrencia.

Tenga en cuenta el uso estratégico de quote_literal() y quote_ident() para desinfectar todo tipo de nombres ilegales (para columnas) y evitar la inyección de SQL .

No se confunda con las múltiples capas de cotización del dólar. Eso es necesario para crear consultas dinámicas. Lo pongo lo más simple posible.