web-dev-qa-db-fra.com

mysql sélectionne les valeurs de lignes dynamiques comme noms de colonnes, une autre colonne comme valeur

J'ai un ancien tableau d'informations sur les utilisateurs (qui est toujours en cours d'utilisation) et je ne peux pas changer la structure de -

id    name       value
------------------------------
0     timezone   Europe/London
0     language   en
0     country    45
0     something  x
1     timezone   Europe/Paris
1     language   fr
1     country    46

fuseau horaire/langue/pays, etc. ne sont que des exemples de noms, ils peuvent être variables/il n'y a pas de liste finie autre que unique sur les lignes de cette colonne

J'ai besoin d'une requête SQL compatible MySQL qui retournerait -

id    timezone       language    country  something
---------------------------------------------------
0     Europe/London  en          45       x
1     Europe/Paris   fr          46

J'ai examiné diverses réponses sur le stackoverflow autour du piratage de la fonctionnalité de table Pivot dans MySQL, et similaire, mais aucune d'entre elles ne semble correspondre à ce cas d'utilisation d'alias de nom de colonne variable à partir de valeurs de ligne uniques d'une colonne de la même table. Même si j'ai peu dormi, ils commencent tous à devenir un peu flous, excuses à l'avance.

Le plus proche que je pourrais trouver serait d'utiliser des instructions préparées https://stackoverflow.com/a/986088/830171 qui obtiendrait d'abord toutes les valeurs possibles/uniques de la colonne de nom et construirait une requête qui utilise CASE WHEN et/ou plusieurs sous -SELECT ou JOIN sur les mêmes requêtes de table.

Les alternatives auxquelles je peux penser seraient d'obtenir toutes les lignes pour cet ID utilisateur et de les traiter dans l'application elle-même dans une boucle for, ou d'essayer de limiter les noms à une quantité finie et d'utiliser sub -SELECTs/JOINs. Cependant, cette deuxième option n'est pas idéale si un nouveau nom est ajouté, je devrais revoir cette requête.

S'il te plaît, dis-moi que j'ai raté quelque chose d'évident

23
gingerCodeNinja

Contrairement à certains autres RDBMS, MySQL n'a pas de support natif pour les opérations de pivotement de ce type par conception (les développeurs estiment qu'il est plus adapté à la présentation, plutôt que base de données, couche de votre application).

Si vous devez absolument effectuer de telles manipulations au sein de MySQL, la construction d'une instruction préparée est le chemin à parcourir - bien que plutôt que de jouer avec CASE, j'utiliserais probablement juste GROUP_CONCAT() de MySQL fonction:

SELECT CONCAT(
  'SELECT `table`.id', GROUP_CONCAT('
     ,    `t_', REPLACE(name, '`', '``'), '`.value
         AS `', REPLACE(name, '`', '``'), '`'
     SEPARATOR ''),
 ' FROM `table` ', GROUP_CONCAT('
     LEFT JOIN `table`   AS `t_', REPLACE(name, '`', '``'), '`
            ON `table`.id = `t_', REPLACE(name, '`', '``'), '`.id
           AND `t_', REPLACE(name, '`', '``'), '`.name = ', QUOTE(name)
     SEPARATOR ''),
 ' GROUP BY `table`.id'
) INTO @qry FROM (SELECT DISTINCT name FROM `table`) t;

PREPARE stmt FROM @qry;
EXECUTE stmt;

Voir sur sqlfiddle .

Notez que le résultat de GROUP_CONCAT() est limité par la variable group_concat_max_len (par défaut de 1024 octets: peu susceptible d'être pertinent ici sauf si vous avez certaines valeurs name extrêmement longues).

40
eggyal