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

PostgreSQL 9.3:tabela dinâmica dinâmica


Você pode fazer isso com crosstab() do módulo adicional tablefunc:
SELECT b
     , COALESCE(a1, 0) AS "A1"
     , COALESCE(a2, 0) AS "A2"
     , COALESCE(a3, 0) AS "A3"
     , ... -- all the way up to "A30"
FROM   crosstab(
         'SELECT colb, cola, 1 AS val FROM matrix
          ORDER  BY 1,2'
        , $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
       ) AS t (b text
             , a1  int, a2  int, a3  int, a4  int, a5  int, a6  int
             , a7  int, a8  int, a9  int, a10 int, a11 int, a12 int
             , a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
             , a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
             , a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);

Se NULL em vez de 0 funciona também, pode ser apenas SELECT * na consulta externa.
Explicação detalhada:
  • Consulta de tabela cruzada PostgreSQL

A "dificuldade" especial aqui:nenhum "valor" real. Então adicione 1 AS val como última coluna.

Número desconhecido de categorias


Uma consulta completamente dinâmica (com tipo de resultado desconhecido) não é possível em uma única consulta. Você precisa de dois consultas. Primeiro construa uma instrução como a acima dinamicamente, depois execute-a. Detalhes:

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

  • PostgreSQL converte colunas em linhas? Transpor?

  • Gerar colunas dinamicamente para crosstab no PostgreSQL

  • Alternativa dinâmica para pivotar com CASE e GROUP BY

Demasiadas categorias


Se você exceder o número máximo de colunas (1600), uma tabela cruzada clássica é impossível, porque o resultado não pode ser representado com colunas individuais. (Além disso, os olhos humanos dificilmente seriam capazes de ler uma tabela com tantas colunas)

Arrays ou tipos de documentos como hstore ou jsonb são a alternativa. Aqui está uma solução com arrays:
SELECT colb, array_agg(cola) AS colas
FROM  (
   SELECT colb, right(colb, -1)::int AS sortb
        , CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
   FROM        (SELECT DISTINCT colb FROM matrix) b
   CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
   LEFT   JOIN matrix m USING (colb, cola)
   ORDER  BY sortb, right(cola, -1)::int 
   ) sub
GROUP  BY 1, sortb
ORDER  BY sortb;

  • Construa a grade completa de valores com:
                (SELECT DISTINCT colb FROM matrix) b
    CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
    

  • LEFT JOIN combinações existentes, ordenar pela parte numérica do nome e agregar em arrays.
    • right(colb, -1)::int corta o caractere inicial de 'A3' e converte os dígitos em inteiro para obtermos uma ordem de classificação adequada.

Matriz básica


Se você quer apenas uma tabela de 0 um 1 onde x = y , isso pode ser mais barato:
SELECT x, array_agg((x = y)::int) AS y_arr
FROM   generate_series(1,10) x
     , generate_series(1,10) y
GROUP  BY 1
ORDER  BY 1;

Fiddle SQL com base no que você forneceu nos comentários.

Observe que sqlfiddle.com atualmente tem um bug que mata a exibição de valores de array. Então eu transmito para text lá para contornar isso.