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

Requête PHP et Mysql, utilisez PHP pour convertir les lignes en colonnes

Il est possible d'obtenir le résultat à l'aide d'une requête SQL, mais ce n'est pas anodin.

Mais avant de vous engager dans cette voie, je vous recommande d'envisager une approche différente.

Étant donné que la requête renvoie un ensemble relativement petit de lignes, vous pouvez à la place récupérer l'intégralité du jeu de résultats dans PHP sous la forme d'un tableau à deux dimensions.

Prenons un cas assez simple, à titre d'illustration :

SELECT foo, fee, fi, fo, fum
  FROM mytable 
 ORDER BY foo

foo fee fi  fo  fum
--- --- --- --- ---
ABC   2   3   5   7
DEF  11  13  17  19

Nous pourrions faire un fetchAll et obtenir un tableau à deux dimensions, puis parcourir le tableau et récupérer les valeurs par colonne plutôt que par ligne. Une option consiste à transformer le tableau que nous recevons en un nouveau tableau qui ressemble à ceci :

bar  ABC  DEF
---  ---  ---
fee    2   11
fi     3   13
fo     5   17
fum    7   19

Il n'est pas vraiment nécessaire de faire la transformation, vous pouvez parcourir le tableau d'origine. Mais séparer la transformation en une étape distincte rendrait probablement votre code un peu plus facile, lorsque vous arriverez à générer réellement la sortie dans la page. (Cela semble être un problème assez courant pour que quelqu'un ait probablement écrit une fonction qui effectue la transformation de tableau que vous souhaitez. Je ne pense pas qu'il existe un PHP intégré qui le fasse.

titres :

array { [0]=>'bar'  [1]=>'ABC'  [2]=>'DEF' }

lignes :

array {
  [0]=>array { [0]=>'fee'   [1]=>'2'  [2]=>'11' }
  [1]=>array { [0]=>'fi'    [1]=>'3'  [2]=>'13' }
  [2]=>array { [0]=>'fo'    [1]=>'5'  [2]=>'17' }
  [3]=>array { [0]=>'fum'   [1]=>'7'  [2]=>'19' }
}

Pour un petit ensemble de lignes comme le vôtre, je choisirais de le faire en PHP plutôt qu'en SQL.

Mais vous avez demandé comment le faire en SQL. Comme je l'ai dit plus tôt, ce n'est pas anodin.

SQL exige que l'instruction SELECT définisse chaque colonne à renvoyer; le nombre et les types des colonnes ne peuvent pas être dynamiques lors de l'exécution de l'instruction.

Si nous construisons une autre requête (en dehors de la requête d'origine) qui définit les colonnes et renvoie les lignes que nous attendons avec des espaces réservés pour les valeurs, nous sommes à mi-chemin. Il ne reste plus qu'à effectuer une jointure externe sur les lignes renvoyées par la requête d'origine et à renvoyer conditionnellement les valeurs de colonne sur les lignes appropriées.

Cette approche fonctionne si vous avez un ensemble prédéfini de lignes et de colonnes dont nous avons besoin, en particulier lorsque la source de ligne d'origine est clairsemée et que nous devons générer les lignes "manquantes". (Par exemple, obtenir le nombre de produits commandés, et il y a beaucoup de lignes manquantes, il n'y a pas de bon moyen de générer les lignes manquantes.

Par exemple :

SELECT r.bar
     , '' AS `ABC`
     , '' AS `DEF`
  FROM ( SELECT 'fee' AS bar
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 GROUP BY r.bar

Cela reviendra :

 bar  ABC  DEF
 ---  ---  ---
 fee
 fi
 fo
 fum

Donc, cela nous donne toutes les colonnes définies et toutes les lignes que nous voulons retourner. La première colonne est remplie. Cette requête n'a pas encore vraiment besoin de GROUP BY, mais nous en aurons besoin une fois que nous ferons correspondre les lignes du "vrai" jeu de résultats source.

L'"astuce" consiste maintenant à faire correspondre les lignes de notre source et à renvoyer la valeur d'une colonne en fonction des conditions appropriées.

Ce que nous allons générer, c'est essentiellement un ensemble de résultats qui ressemble à ceci :

bar  foo  ABC  DEF
---  ---  ---  ---
fee  ABC    2
fee  DEF        11
fi   ABC    3
fi   DEF        13
fo   ABC    5
fo   DEF        15
fum  ABC    7
fum  DEF        17

Ensuite, nous allons "réduire" les lignes, en supprimant la colonne foo du jeu de résultats et en faisant un GROUP BY sur bar . Nous allons utiliser une fonction d'agrégation (soit MAX ou SUM) en tirant parti de la manipulation qu'ils font avec les valeurs NULL, pour produire un résultat comme celui-ci :

bar  foo  ABC  DEF
---  ---  ---  ---
fee         2   11
fi          3   13
fo          5   15
fum         7   17

En utilisant ce SQL plutôt lourd :

SELECT r.bar
     , MAX(CASE WHEN t.foo = 'ABC' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'ABC'
     , MAX(CASE WHEN t.foo = 'DEF' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'DEF'
  FROM ( SELECT 'foo' AS col
          UNION ALL SELECT 'fee'
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 CROSS
  JOIN mysource t
 GROUP BY r.bar

Notez que mysource dans la requête ci-dessus peut être remplacée par une vue en ligne, en enveloppant des parenthèses autour d'une requête appropriée qui renvoie les lignes souhaitées.

La vue en ligne alias r est notre source pour renvoyer les lignes que nous voulons renvoyer.

Les expressions de la liste SELECT effectuent les tests conditionnels, pour sélectionner les bonnes valeurs pour chaque colonne de chaque ligne.

Étant donné le modèle régulier des instructions CASE, il est possible d'utiliser du SQL pour aider à générer la requête, mais cela doit être fait dans une étape distincte. La sortie de ce SQL peut être utilisée pour aider à former la requête dont nous avons besoin.

Dans votre cas, étant donné que workdate est ce que vous voulez utiliser pour l'en-tête de colonne, cela devra probablement être généré dynamiquement. (Vous pouvez supprimer cette deuxième colonne "jour de la semaine" de la requête source d'origine et la déplacer vers la requête externe.

Si je ne connaissais pas la workdate valeurs pour les en-têtes avant d'exécuter la requête, alors je choisirais de créer une TEMPORARY TABLE et de la remplir avec les résultats de la requête d'origine, puis d'interroger la table temporaire pour obtenir le workdate en-têtes et la "première colonne" pour générer les lignes. Ensuite, j'exécuterais la requête réelle sur la table temporaire.

Pour répéter, je pense que vous feriez mieux de faire la transformation/pivot des résultats de votre requête d'origine en PHP, plutôt que d'essayer de le faire en SQL.