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.