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

Selecionando vários valores max() usando uma única instrução SQL


Mais uma vez, para mais do que apenas alguns "tipos de dados", sugiro 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)

Devoluções:
type | Final Fantasy | Quake 3 | World of Warcraft
-----+---------------+---------+-------------------
max  | 500           | 1500    |    1200

Mais explicações para o básico:
Consulta de tabela cruzada PostgreSQL

Solução dinâmica


O complicado é tornar isso completamente dinâmico :para fazê-lo funcionar para
  • um número desconhecido de colunas (data_types neste caso)
  • com nomes desconhecidos (data_types novamente)

Pelo menos o tipo é bem conhecido:integer nesse caso.

Resumindo:isso não é possível com o PostgreSQL atual (incluindo 9.3). Existem aproximações com tipos polimórficos e formas de contornar as restrições com arrays ou tipos hstore. Pode ser bom o suficiente para você. Mas é estritamente impossível para obter o resultado com colunas individuais em uma única consulta SQL. O SQL é muito rígido em relação aos tipos e quer saber o que esperar de volta.

No entanto , isso pode ser feito com dois consultas. O primeiro cria a consulta real a ser usada. Com base no caso simples acima:
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

Isso gera a consulta que você realmente precisa. Execute o segundo dentro da mesma transação para evitar problemas de simultaneidade.

Observe o uso estratégico de quote_literal() e quote_ident() para limpar todos os tipos de nomes ilegais (para colunas) e impedir a injeção de SQL .

Não se confunda com várias camadas de cotação de dólares. Isso é necessário para criar consultas dinâmicas. Coloquei o mais simples possível.