Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySQL - Linhas para Colunas


Vou adicionar uma explicação um pouco mais longa e detalhada das etapas a serem seguidas para resolver esse problema. Peço desculpas se for muito longo.

Vou começar com a base que você deu e usá-la para definir alguns termos que usarei no restante deste post. Esta será a tabela base :
select * from history;

+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
|      1 | A        |        10 |
|      1 | B        |         3 |
|      2 | A        |         9 |
|      2 | C        |        40 |
+--------+----------+-----------+

Este será nosso objetivo, a bela tabela dinâmica :
select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

Valores no history.hostid coluna se tornará valores y na tabela dinâmica. Valores no history.itemname coluna se tornará valores x (por razões óbvias).

Quando tenho que resolver o problema de criar uma tabela dinâmica, resolvo isso usando um processo de três etapas (com uma quarta etapa opcional):
  1. selecione as colunas de interesse, ou seja, valores y e valores x
  2. estende a tabela base com colunas extras -- uma para cada valor x
  3. agrupe e agregue a tabela estendida -- um grupo para cada valor y
  4. (opcional) embelezar a tabela agregada

Vamos aplicar estas etapas ao seu problema e ver o que obtemos:

Etapa 1:selecione as colunas de interesse . No resultado desejado, hostid fornece os valores y e itemname fornece os valores x .

Etapa 2:estender a tabela base com colunas extras . Normalmente, precisamos de uma coluna por valor x. Lembre-se de que nossa coluna de valor x é itemname :
create view history_extended as (
  select
    history.*,
    case when itemname = "A" then itemvalue end as A,
    case when itemname = "B" then itemvalue end as B,
    case when itemname = "C" then itemvalue end as C
  from history
);

select * from history_extended;

+--------+----------+-----------+------+------+------+
| hostid | itemname | itemvalue | A    | B    | C    |
+--------+----------+-----------+------+------+------+
|      1 | A        |        10 |   10 | NULL | NULL |
|      1 | B        |         3 | NULL |    3 | NULL |
|      2 | A        |         9 |    9 | NULL | NULL |
|      2 | C        |        40 | NULL | NULL |   40 |
+--------+----------+-----------+------+------+------+

Observe que não alteramos o número de linhas -- apenas adicionamos colunas extras. Observe também o padrão de NULL s -- uma linha com itemname = "A" tem um valor não nulo para a nova coluna A e valores nulos para as outras novas colunas.

Etapa 3:agrupe e agregue a tabela estendida . Precisamos group by hostid , uma vez que fornece os valores de y:
create view history_itemvalue_pivot as (
  select
    hostid,
    sum(A) as A,
    sum(B) as B,
    sum(C) as C
  from history_extended
  group by hostid
);

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 | NULL |
|      2 |    9 | NULL |   40 |
+--------+------+------+------+

(Observe que agora temos uma linha por valor y.) Ok, estamos quase lá! Nós só precisamos nos livrar daqueles feios NULL s.

Etapa 4:embelezar . Vamos apenas substituir quaisquer valores nulos por zeros para que o conjunto de resultados seja mais agradável de se ver:
create view history_itemvalue_pivot_pretty as (
  select 
    hostid, 
    coalesce(A, 0) as A, 
    coalesce(B, 0) as B, 
    coalesce(C, 0) as C 
  from history_itemvalue_pivot 
);

select * from history_itemvalue_pivot_pretty;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

E nós terminamos -- nós construímos uma tabela dinâmica legal e bonita usando o MySQL.

Considerações ao aplicar este procedimento:
  • qual valor usar nas colunas extras. Eu usei itemvalue neste exemplo
  • qual valor "neutro" usar nas colunas extras. Eu usei NULL , mas também pode ser 0 ou "" , dependendo da sua situação exata
  • qual função de agregação usar ao agrupar. Eu usei sum , mas count e max também são frequentemente usados ​​(max é frequentemente usado ao construir "objetos" de uma linha que foram espalhados por muitas linhas)
  • usando várias colunas para valores y. Esta solução não se limita a usar uma única coluna para os valores y -- basta conectar as colunas extras ao group by cláusula (e não se esqueça de select eles)

Limitações conhecidas:
  • esta solução não permite n colunas na tabela dinâmica -- cada coluna dinâmica precisa ser adicionada manualmente ao estender a tabela base. Então, para 5 ou 10 valores x, esta solução é boa. Por 100, não é tão bom. Existem algumas soluções com procedimentos armazenados gerando uma consulta, mas são feias e difíceis de acertar. Atualmente, não conheço uma boa maneira de resolver esse problema quando a tabela dinâmica precisa ter muitas colunas.