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

Selecione linhas MYSQL, mas linhas em colunas e colunas em linhas


Com colunas fixas e conhecidas, veja como fazer (tomei a liberdade de nomear a tabela "notas"):

Ideia geral:


Para criar uma união de diferentes consultas e executá-la.

Como você precisa de dados reais como cabeçalhos de coluna, a primeira parte da união será semelhante a:
SELECT 'id', '1', '2', ....

Essa consulta sozinha duplicará o resultado, portanto, precisamos informar ao MySQL que precisamos ter 0 linhas adicionando LIMIT 0, 0 .

Nossa primeira linha da união conterá 'Name' , bem como todos os dados da coluna "Nome" da tabela. Para obter essa linha, precisamos de uma 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 a mesma lógica, nossa segunda linha ficará assim:
SELECT 'Marks',
    (SELECT Marks FROM grades LIMIT 0, 1),
    (SELECT Marks FROM grades LIMIT 1, 1),
    (SELECT Marks FROM grades LIMIT 2, 1),
    ...

Obtendo o cabeçalho:


Precisamos produzir uma linha do MySQL como:
SELECT 'id', '1', '2', ... LIMIT 0, 0;

Para obter essa linha, usaremos CONCAT() e GROUP_CONCAT() funções:
SELECT 'id', 
    (SELECT GROUP_CONCAT(CONCAT(' \'', id, '\'')) FROM grades)
LIMIT 0, 0;

e vamos armazenar essa linha em uma nova variável:
SET @header = CONCAT('SELECT \'id\', ',
    (SELECT GROUP_CONCAT(CONCAT(' \'', id, '\'')) FROM grades),
    ' LIMIT 0, 0');

Criando as linhas:


Precisamos criar duas consultas como a seguinte:
SELECT 'Name',
    (SELECT Name FROM grades LIMIT 0, 1),
    (SELECT Name FROM grades LIMIT 1, 1),
    (SELECT Name FROM grades LIMIT 2, 1),
    ...

Como não sabemos de antemão quantas linhas existem em nossa tabela original, usaremos variáveis ​​para gerar os diferentes LIMIT x, 1 declarações. Eles podem ser produzidos usando o seguinte:
SET @a = -1;
SELECT @a:[email protected]+1 FROM grades;

Usando este snippet, podemos criar nossas subconsultas:
SELECT GROUP_CONCAT(
    CONCAT(' (SELECT name FROM grades LIMIT ',
        @a:[email protected]+1,
        ', 1)')
    )
FROM grades

Que colocaremos em uma variável names @line1, junto com os dados da primeira coluna (que é o nome da segunda coluna):
SET @a = -1;
SET @line1 = CONCAT(
    'SELECT \'Name\',',
    (
        SELECT GROUP_CONCAT(
            CONCAT(' (SELECT Name FROM grades LIMIT ',
                @a:[email protected]+1,
                ', 1)')
            )
        FROM grades
    ));

Seguindo a mesma lógica, a segunda linha 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
    ));

Combinando todos eles:


Nossas três variáveis ​​agora contêm:
@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)

Nós só precisamos criar uma variável final usando CONCAT() , prepare-o como uma nova consulta e execute-o:
SET @query = CONCAT('(',
    @header,
    ') UNION (',
    @line1,
    ') UNION (',
    @line2,
    ')'
);

PREPARE my_query FROM @query;
EXECUTE my_query;

Solução completa:


(para teste e referência):
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;

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

Conclusões finais:


  • Ainda não sei por que você precisa transformar linhas em colunas, e tenho certeza que a solução que apresentei não é a melhor (em termos de desempenho).

  • Você pode até usar minha solução como um começo e adaptá-la a uma solução de uso geral onde os nomes das colunas da tabela (e o número de linhas) não são conhecidos, usando information_schema .COLUMNS como fonte, mas acho que isso é ir longe demais.

  • Acredito fortemente que é muito melhor colocar a tabela original em um array e depois girar esse array, obtendo assim os dados no formato desejado.