Mysql
 sql >> Base de données >  >> RDS >> Mysql

problème avec le tableau croisé dynamique mysql

Lorsque vous essayez de faire pivoter une valeur dynamique ou inconnue, je vous suggère toujours de commencer par une version statique ou codée en dur de la requête, puis de la convertir en SQL dynamique.

MySQL n'a pas de fonction PIVOT, vous devrez donc utiliser une fonction d'agrégation avec une expression CASE pour obtenir le résultat. La version statique du code ressemblera à ce qui suit :

select t.id teamid, 
  t.name teamname, 
  p.id processid, 
  p.name processname,
  max(case when pd.keyname = 'shape' then tpd.value end) shape,
  max(case when pd.keyname = 'vegetable' then tpd.value end) vegetable,
  max(case when pd.keyname = 'fruit' then tpd.value end) fruit,
  max(case when pd.keyname = 'animal' then tpd.value end) animal
from teams t
inner join teamprocesses tp
  on t.id = tp.teamid
inner join TeamProcessDetails tpd
  on tp.id = tpd.teamProcessId
inner join processes p
  on tp.processid = p.id
inner join processdetails pd
  on p.id = pd.processid
  and tpd.processDetailsid = pd.id
group by t.id, t.name, p.id, p.name;

Voir SQL Fiddle avec démo .

Maintenant, si vous allez avoir un nombre inconnu de keynames que vous souhaitez convertir en colonnes, vous devrez alors utiliser un déclaration préparée pour générer du SQL dynamique. Le code ressemblera à :

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when pd.keyname = ''',
      keyname,
      ''' then tpd.value end) AS ',
      replace(keyname, ' ', '')
    )
  ) INTO @sql
from ProcessDetails;

SET @sql 
    = CONCAT('SELECT t.id teamid, 
                t.name teamname, 
                p.id processid, 
                p.name processname, ', @sql, ' 
              from teams t
              inner join teamprocesses tp
                on t.id = tp.teamid
              inner join TeamProcessDetails tpd
                on tp.id = tpd.teamProcessId
              inner join processes p
                on tp.processid = p.id
              inner join processdetails pd
                on p.id = pd.processid
                and tpd.processDetailsid = pd.id
              group by t.id, t.name, p.id, p.name;');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Voir SQL Fiddle avec démo .

Une chose à garder à l'esprit le GROUP_CONCAT la fonction pour créer la chaîne de colonnes a une longueur maximale par défaut de 1024, donc si vous allez avoir beaucoup de caractères dans cette chaîne, vous devrez peut-être modifier la valeur de session pour le group_concat_max_len .

Cette requête donnera un résultat :

| TEAMID | TEAMNAME | PROCESSID | PROCESSNAME |  SHAPE | VEGETABLE |  FRUIT | ANIMAL |
|      1 |    teamA |         1 |    processA | circle |    carrot |  apple | (null) |
|      1 |    teamA |         2 |    processB | (null) |    (null) | (null) |    dog |