Het is mogelijk om het resultaat te krijgen met een SQL-query, maar het is niet triviaal.
Maar voordat je die weg inslaat, raad ik je aan een andere aanpak te overwegen.
Aangezien de query een relatief kleine set rijen retourneert, kunt u in plaats daarvan de volledige resultatenset in PHP ophalen als een tweedimensionale array.
Overweeg een vrij eenvoudig geval, ter illustratie:
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
We zouden een fetchAll kunnen doen en een tweedimensionale array kunnen krijgen, dan door de array kunnen lopen en de waarden kolomsgewijs kunnen ophalen in plaats van rijsgewijs. Een optie is om de array die we ontvangen om te zetten in nieuwe arrays die er als volgt uitzien:
bar ABC DEF
--- --- ---
fee 2 11
fi 3 13
fo 5 17
fum 7 19
Het is niet echt nodig om de transformatie uit te voeren, je zou de originele array kunnen lopen. Maar het scheiden van de transformatie als een afzonderlijke stap zou uw code waarschijnlijk een beetje gemakkelijker maken, wanneer u de uitvoer daadwerkelijk naar de pagina gaat genereren. (Het lijkt een veel voorkomend probleem dat iemand waarschijnlijk een functie heeft geschreven die de array-transformatie doet die je wilt. Ik denk niet dat er een ingebouwde PHP is die dit doet.
koppen:
array { [0]=>'bar' [1]=>'ABC' [2]=>'DEF' }
rijen:
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' }
}
Voor een kleine set rijen zoals jij, zou ik ervoor kiezen om dit in PHP te doen in plaats van in SQL.
Maar je vroeg hoe je het in SQL moest doen. Zoals ik al eerder zei, het is niet triviaal.
SQL vereist dat de SELECT-instructie elke te retourneren kolom definieert; het aantal en de typen van de kolommen kunnen niet dynamisch zijn wanneer de instructie wordt uitgevoerd.
Als we een andere query maken (naast de oorspronkelijke query) die de kolommen definieert en de rijen retourneert waarvan we verwachten dat ze worden geretourneerd met tijdelijke aanduidingen voor de waarden, zijn we halverwege. Het enige dat overblijft is om een outer join uit te voeren op de rijen die zijn geretourneerd door de oorspronkelijke query en de kolomwaarden voorwaardelijk op de juiste rijen te retourneren.
Deze aanpak werkt als u een vooraf gedefinieerde set rijen en kolommen hebt die we moeten retourneren, vooral wanneer de oorspronkelijke rijbron schaars is en we de "ontbrekende" rijen moeten genereren. (Als u bijvoorbeeld het aantal bestelde producten krijgt en er veel ontbrekende rijen zijn, is er geen goede manier om de ontbrekende rijen te genereren.
Bijvoorbeeld:
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
Dat zal terugkeren:
bar ABC DEF
--- --- ---
fee
fi
fo
fum
Dus dat geeft ons alle gedefinieerde kolommen en alle rijen die we willen retourneren. De eerste kolom is gevuld. Die zoekopdracht heeft de GROUP BY nog niet echt nodig, maar we zullen hem nodig hebben zodra we overeenkomen met de rijen in de "echte" bronresultatenset.
De "truc" is nu het matchen van de rijen uit onze bron en het retourneren van de waarde uit een kolom op basis van de juiste voorwaarden.
Wat we gaan genereren, is in wezen een resultatenset die er als volgt uitziet:
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
Vervolgens gaan we de rijen "samenvouwen", door de foo-kolom uit de resultatenset te verwijderen en een GROUP BY op bar
te doen . We gaan een aggregatiefunctie gebruiken (ofwel MAX of SUM) die gebruik maakt van de verwerking die ze doen met NULL-waarden, om een resultaat als dit te produceren:
bar foo ABC DEF
--- --- --- ---
fee 2 11
fi 3 13
fo 5 15
fum 7 17
Deze nogal onhandige SQL gebruiken:
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
Merk op dat mysource
in de bovenstaande query kan worden vervangen door een inline-weergave, waarbij de haakjes rond een geschikte query worden gewikkeld die de gewenste rijen retourneert.
De inline-weergave met een alias als r
is onze bron voor het retourneren van de rijen die we willen retourneren.
De expressies in de SELECT-lijst doen de voorwaardelijke tests om de juiste waarden voor elke kolom in elke rij te kiezen.
Gezien het regelmatige patroon van de CASE-instructies, is het mogelijk om wat SQL te gebruiken om de query te genereren, maar dat moet als een aparte stap worden gedaan. De uitvoer van die SQL kan worden gebruikt om de daadwerkelijke query te vormen die we nodig hebben.
In jouw geval, gezien het feit dat workdate
is wat u wilt gebruiken voor de kolomkop, dit zal waarschijnlijk dynamisch moeten worden gegenereerd. (Je zou die tweede kolom 'dag van de week'-kolom uit de oorspronkelijke bronquery kunnen verwijderen en die naar de buitenste query kunnen verplaatsen.
Als ik de workdate
niet wist waarden voor de koppen voordat ik de query uitvoerde, dan zou ik ervoor kiezen om een TIJDELIJKE TABEL te maken en deze te vullen met resultaten van de oorspronkelijke query, en vervolgens de tijdelijke tabel te doorzoeken om de workdate
te krijgen headers en de "eerste kolom" om de rijen te genereren. Dan zou ik de eigenlijke query uitvoeren op de tijdelijke tabel.
Om te herhalen, ik denk dat je beter de transformatie/pivot van de resultaten van je oorspronkelijke query in PHP kunt doen, in plaats van te proberen het in SQL te doen.