sql >> Database >  >> RDS >> Mysql

PHP- en Mysql-query, gebruik PHP om rij naar kolommen te converteren

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.



  1. Hoe SYS_GUID() werkt in MariaDB

  2. De codering van Griekse tekens werkt in HTML, maar niet in PHP

  3. SELECT FOR UPDATE met de hele tabel in MySQL in plaats van rij voor rij

  4. Alle rijen retourneren met bepaalde voorwaarden