sql >> Base de Datos >  >> RDS >> Mysql

MySQL - Filas a Columnas

Voy a añadir una explicación algo más larga y detallada de los pasos a seguir para solucionar este problema. Me disculpo si es demasiado largo.

Comenzaré con la base que me diste y la usaré para definir un par de términos que usaré en el resto de esta publicación. Esta será la tabla base :

select * from history;

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

Este será nuestro objetivo, la bonita tabla dinámica :

select * from history_itemvalue_pivot;

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

Valores en history.hostid la columna se convertirá en valores y en la tabla dinámica. Valores en history.itemname la columna se convertirá en valores x (por razones obvias).

Cuando tengo que resolver el problema de crear una tabla dinámica, lo afronto mediante un proceso de tres pasos (con un cuarto paso opcional):

  1. seleccione las columnas de interés, es decir, valores y y valores x
  2. amplíe la tabla base con columnas adicionales, una para cada valor x
  3. agrupar y agregar la tabla extendida:un grupo para cada valor de y
  4. (opcional) embellecer la tabla agregada

Apliquemos estos pasos a su problema y veamos qué obtenemos:

Paso 1:selecciona columnas de interés . En el resultado deseado, hostid proporciona los valores y y itemname proporciona los valores x .

Paso 2:amplía la tabla base con columnas adicionales . Por lo general, necesitamos una columna por valor de x. Recuerde que nuestra columna de valor x es 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 |
+--------+----------+-----------+------+------+------+

Tenga en cuenta que no cambiamos el número de filas, solo agregamos columnas adicionales. También tenga en cuenta el patrón de NULL s -- una fila con itemname = "A" tiene un valor no nulo para la nueva columna A y valores nulos para las otras columnas nuevas.

Paso 3:agrupar y agregar la tabla extendida . Necesitamos group by hostid , ya que proporciona los 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 |
+--------+------+------+------+

(Tenga en cuenta que ahora tenemos una fila por valor y). Bien, ¡ya casi llegamos! Solo tenemos que deshacernos de esos feos NULL s.

Paso 4:embellecer . Simplemente vamos a reemplazar cualquier valor nulo con ceros para que el conjunto de resultados sea más agradable de 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 |
+--------+------+------+------+

Y hemos terminado:hemos creado una tabla dinámica agradable y bonita con MySQL.

Consideraciones al aplicar este procedimiento:

  • qué valor usar en las columnas adicionales. Usé itemvalue en este ejemplo
  • qué valor "neutral" usar en las columnas adicionales. Usé NULL , pero también podría ser 0 o "" , dependiendo de su situación exacta
  • qué función agregada utilizar al agrupar. Usé sum , pero count y max también se utilizan a menudo (max se usa a menudo cuando se construyen "objetos" de una fila que se han distribuido en muchas filas)
  • utilizando varias columnas para los valores de y. Esta solución no se limita a usar una sola columna para los valores de y, simplemente inserte las columnas adicionales en el group by cláusula (y no olvide select ellos)

Limitaciones conocidas:

  • esta solución no permite n columnas en la tabla dinámica:cada columna dinámica debe agregarse manualmente al extender la tabla base. Entonces, para 5 o 10 valores de x, esta solución es buena. Por 100, no tan agradable. Hay algunas soluciones con procedimientos almacenados que generan una consulta, pero son desagradables y difíciles de corregir. Actualmente no conozco una buena manera de resolver este problema cuando la tabla dinámica necesita tener muchas columnas.