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

Seleccione filas MYSQL pero filas en columnas y columna en filas

Con columnas fijas y conocidas, así es como se hace (me tomé la libertad de nombrar la tabla "grados"):

Idea general:

Para crear una unión de diferentes consultas y ejecutarla.

Dado que necesita datos reales como encabezados de columna, la primera parte de la unión se verá así:

SELECT 'id', '1', '2', ....

Solo esa consulta duplicará el resultado, por lo tanto, debemos decirle a MySQL que necesitamos tener 0 filas agregando LIMIT 0, 0 .

Nuestra primera fila de la unión contendrá 'Name' , así como todos los datos de la columna "Nombre" de la tabla. Para obtener esa línea necesitamos una consulta como:

SELECT 'Name',
    (SELECT Name FROM grades LIMIT 0, 1),
    (SELECT Name FROM grades LIMIT 1, 1),
    (SELECT Name FROM grades LIMIT 2, 1),
    ...

Usando la misma lógica, nuestra segunda fila se verá así:

SELECT 'Marks',
    (SELECT Marks FROM grades LIMIT 0, 1),
    (SELECT Marks FROM grades LIMIT 1, 1),
    (SELECT Marks FROM grades LIMIT 2, 1),
    ...

Obteniendo el encabezado:

Necesitamos producir una fila de MySQL como:

SELECT 'id', '1', '2', ... LIMIT 0, 0;

Para obtener esa línea usaremos CONCAT() y GROUP_CONCAT() funciones:

SELECT 'id', 
    (SELECT GROUP_CONCAT(CONCAT(' \'', id, '\'')) FROM grades)
LIMIT 0, 0;

y vamos a almacenar esa línea en una nueva variable:

SET @header = CONCAT('SELECT \'id\', ',
    (SELECT GROUP_CONCAT(CONCAT(' \'', id, '\'')) FROM grades),
    ' LIMIT 0, 0');

Creando las líneas:

Necesitamos crear dos consultas como las siguientes:

SELECT 'Name',
    (SELECT Name FROM grades LIMIT 0, 1),
    (SELECT Name FROM grades LIMIT 1, 1),
    (SELECT Name FROM grades LIMIT 2, 1),
    ...

Como no sabemos de antemano cuántas filas hay en nuestra tabla original, usaremos variables para generar los diferentes LIMIT x, 1 declaraciones. Se pueden producir usando lo siguiente:

SET @a = -1;
SELECT @a:[email protected]+1 FROM grades;

Usando este fragmento, podemos crear nuestras subconsultas:

SELECT GROUP_CONCAT(
    CONCAT(' (SELECT name FROM grades LIMIT ',
        @a:[email protected]+1,
        ', 1)')
    )
FROM grades

Los cuales pondremos en una variable llamada @line1, junto con los datos de la primera columna (que es el nombre de la segunda columna):

SET @a = -1;
SET @line1 = CONCAT(
    'SELECT \'Name\',',
    (
        SELECT GROUP_CONCAT(
            CONCAT(' (SELECT Name FROM grades LIMIT ',
                @a:[email protected]+1,
                ', 1)')
            )
        FROM grades
    ));

Siguiendo la misma lógica, la segunda línea será:

SET @a := -1;
SET @line2 = CONCAT(
    'SELECT \'Marks\',',
    (
        SELECT GROUP_CONCAT(
            CONCAT(' (SELECT Marks FROM grades LIMIT ',
                @a:[email protected]+1,
                ', 1)')
            )
        FROM grades
    ));

Combinándolos todos:

Nuestras tres variables ahora contienen:

@header:
SELECT 'id',  '1', '2' LIMIT 0, 0

@line1:
SELECT 'Name', (SELECT Name FROM grades LIMIT 0, 1),
    (SELECT name FROM grades LIMIT 1, 1)

@line2:
SELECT 'Marks', (SELECT Marks FROM grades LIMIT 0, 1),
    (SELECT marks FROM grades LIMIT 1, 1)

Solo necesitamos crear una variable final usando CONCAT() , prepárelo como una nueva consulta y ejecútelo:

SET @query = CONCAT('(',
    @header,
    ') UNION (',
    @line1,
    ') UNION (',
    @line2,
    ')'
);

PREPARE my_query FROM @query;
EXECUTE my_query;

Solución completa:

(para prueba y referencia):

SET @header = CONCAT('SELECT \'id\', ',
    (SELECT GROUP_CONCAT(CONCAT(' \'', id, '\'')) FROM grades),
    ' LIMIT 0, 0');

SET @a = -1;
SET @line1 = CONCAT(
    'SELECT \'Name\',',
    (
        SELECT GROUP_CONCAT(
            CONCAT(' (SELECT Name FROM grades LIMIT ',
                @a:[email protected]+1,
                ', 1)')
            )
        FROM grades
    ));

SET @a := -1;
SET @line2 = CONCAT(
    'SELECT \'Marks\',',
    (
        SELECT GROUP_CONCAT(
            CONCAT(' (SELECT Marks FROM grades LIMIT ',
                @a:[email protected]+1,
                ', 1)')
            )
        FROM grades
    ));

SET @query = CONCAT('(',
    @header,
    ') UNION (',
    @line1,
    ') UNION (',
    @line2,
    ')'
);

PREPARE my_query FROM @query;
EXECUTE my_query;

Salida:

+-------+------+-------+
| id    | 1    | 2     |
+-------+------+-------+
| Name  | Ram  | Shyam |
| Marks | 45   | 87    |
+-------+------+-------+
2 rows in set (0.00 sec)

Pensamientos finales:

  • Todavía no estoy seguro de por qué necesita transformar filas en columnas, y estoy seguro de que la solución que presenté no es la mejor (en términos de rendimiento).

  • Incluso puede usar mi solución como un comienzo y adaptarla a una solución de propósito general donde los nombres de las columnas de la tabla (y el número de líneas) no se conocen, usando information_schema .COLUMNS como fuente, pero supongo que eso es ir demasiado lejos.

  • Creo firmemente que es mucho mejor poner la tabla original en una matriz y luego rotar esa matriz, obteniendo así los datos en el formato deseado.