PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Sélection de plusieurs valeurs max() à l'aide d'une seule instruction SQL

Encore une fois, pour plus que quelques "types de données", je suggère d'utiliser 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)

Renvoie :

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

Plus d'explications pour les bases :
Requête croisée PostgreSQL

Solution dynamique

La difficulté est de rendre cela complètement dynamique :pour le faire fonctionner

  • un numéro inconnu de colonnes (data_types dans ce cas)
  • avec des noms inconnus (data_types à nouveau)

Au moins le type est bien connu :integer dans ce cas.

En bref :ce n'est pas possible avec PostgreSQL actuel (y compris 9.3). Il existe des approximations avec des types polymorphes et des moyens de contourner les restrictions avec des tableaux ou des types hstore. Peut être assez bon pour vous. Mais c'est strictement impossible pour obtenir le résultat avec des colonnes individuelles dans une seule requête SQL. SQL est très rigide sur les types et veut savoir à quoi s'attendre en retour.

Cependant , cela peut être fait avec deux requêtes. Le premier construit la requête réelle à utiliser. S'appuyant sur le cas simple ci-dessus :

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

Cela génère la requête dont vous avez réellement besoin. Exécutez le second dans la même transaction pour éviter les problèmes de simultanéité.

Notez l'utilisation stratégique de quote_literal() et quote_ident() pour nettoyer toutes sortes de noms illégaux (pour les colonnes) et empêcher l'injection SQL .

Ne vous laissez pas confondre par plusieurs couches de cotations en dollars. Cela est nécessaire pour créer des requêtes dynamiques. Je l'ai dit aussi simple que possible.