Mysql
 sql >> Database >  >> RDS >> Mysql

Seleziona le righe MySQL ma le righe in colonne e le colonne in righe

Con una colonna fissa e nota, ecco come fare (mi sono preso la libertà di nominare la tabella "voti"):

Idea generale:

Per creare un'unione di query diverse ed eseguirla.

Poiché hai bisogno di dati effettivi come intestazioni di colonna, la prima parte dell'unione sarà simile a:

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

Quella query da sola duplicherà il risultato, quindi dobbiamo dire a MySQL che dobbiamo avere 0 righe aggiungendo LIMIT 0, 0 .

La nostra prima riga dell'unione conterrà 'Name' , così come tutti i dati dalla colonna "Nome" della tabella. Per ottenere quella riga abbiamo bisogno di una query come:

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 stessa logica, la nostra seconda riga sarà simile a:

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

Ottenere l'intestazione:

Dobbiamo produrre una riga da MySQL come:

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

Per ottenere quella riga useremo CONCAT() e GROUP_CONCAT() funzioni:

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

e memorizzeremo quella riga in una nuova variabile:

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

Creazione delle linee:

Dobbiamo creare due query come le seguenti:

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

Poiché non sappiamo in anticipo quante righe ci sono nella nostra tabella originale, utilizzeremo variabili per generare il diverso LIMIT x, 1 dichiarazioni. Possono essere prodotti utilizzando quanto segue:

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

Usando questo snippet, possiamo creare le nostre sottoquery:

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

Che inseriremo in una variabile nomina @line1, insieme ai dati della prima colonna (che è il nome della seconda colonna):

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

Seguendo la stessa logica, la seconda riga sarà:

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

Combinandoli tutti:

Le nostre tre variabili ora contengono:

@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)

Dobbiamo solo creare una variabile finale usando CONCAT() , preparalo come una nuova query ed eseguilo:

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

PREPARE my_query FROM @query;
EXECUTE my_query;

Intera soluzione:

(per test e riferimento):

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;

Uscita:

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

Pensieri conclusivi:

  • Non sono ancora sicuro del motivo per cui è necessario trasformare le righe in colonne e sono sicuro che la soluzione che ho presentato non è la migliore (in termini di prestazioni).

  • Puoi persino utilizzare la mia soluzione come inizio e adattarla a una soluzione generica in cui i nomi delle colonne della tabella (e il numero di righe) non sono noti, utilizzando information_schema .COLUMNS come fonte, ma immagino che stia andando troppo oltre.

  • Credo fermamente che sia molto meglio inserire la tabella originale in un array e quindi ruotare quell'array, ottenendo così i dati nel formato desiderato.