PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Selezione di più valori max() utilizzando una singola istruzione SQL

Ancora una volta, per più di alcuni "tipi di dati", suggerisco di utilizzare 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)

Resi:

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

Ulteriori spiegazioni per le nozioni di base:
Query a campi incrociati PostgreSQL

Soluzione dinamica

La cosa difficile è rendere questo completamente dinamico :per farlo funzionare

  • un numero sconosciuto di colonne (data_types in questo caso)
  • con nomi sconosciuti (di nuovo tipi_dati)

Almeno il tipo è noto:integer in questo caso.

In breve:questo non è possibile con l'attuale PostgreSQL (incluso 9.3). Esistono approssimazioni con tipi polimorfici e modi per aggirare le restrizioni con array o tipi hstore. Potrebbe essere abbastanza buono per te. Ma è rigorosamente impossibile per ottenere il risultato con singole colonne in una singola query SQL. SQL è molto rigido sui tipi e vuole sapere cosa aspettarsi in cambio.

Tuttavia , può essere fatto con due interrogazioni. Il primo crea la query effettiva da utilizzare. Basandosi sul semplice caso di cui sopra:

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

Questo genera la query di cui hai effettivamente bisogno. Esegui il secondo all'interno della stessa transazione per evitare problemi di concorrenza.

Nota l'uso strategico di quote_literal() e quote_ident() per disinfettare tutti i tipi di nomi illegali (per colonne) e prevenire SQL injection .

Non lasciarti confondere da più livelli di quotazione del dollaro. È necessario per creare query dinamiche. Lo metto il più semplice possibile.