Mysql
 sql >> Datenbank >  >> RDS >> Mysql

PHP- und MySQL-Abfrage, verwenden Sie PHP, um Zeilen in Spalten zu konvertieren

Es ist möglich, das Ergebnis mit einer SQL-Abfrage zu erhalten, aber es ist nicht trivial.

Aber bevor Sie diesen Weg gehen, empfehle ich Ihnen, einen anderen Ansatz in Betracht zu ziehen.

Da die Abfrage eine relativ kleine Menge von Zeilen zurückgibt, könnten Sie stattdessen die gesamte Ergebnismenge als zweidimensionales Array in PHP abrufen.

Betrachten Sie zur Veranschaulichung einen ziemlich einfachen Fall:

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

Wir könnten ein fetchAll ausführen und ein zweidimensionales Array erhalten, dann das Array durchlaufen und die Werte spaltenweise statt zeilenweise abrufen. Eine Möglichkeit besteht darin, das Array, das wir erhalten, in ein neues Array umzuwandeln, das wie folgt aussieht:

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

Es ist nicht wirklich notwendig, die Transformation durchzuführen, Sie könnten das ursprüngliche Array durchlaufen. Aber die Umwandlung in einen separaten Schritt zu unterteilen, würde Ihren Code wahrscheinlich etwas einfacher machen, wenn Sie die Ausgabe tatsächlich auf der Seite generieren. (Es scheint ein ziemlich häufiges Problem zu sein, dass jemand wahrscheinlich eine Funktion geschrieben hat, die die gewünschte Array-Transformation durchführt. Ich glaube nicht, dass es ein PHP-Buildin gibt, das dies tut.

Überschriften:

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

Zeilen:

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' }
}

Für eine kleine Reihe von Zeilen, wie Sie sie haben, würde ich mich dafür entscheiden, dies in PHP statt in SQL zu tun.

Aber Sie haben gefragt, wie man es in SQL macht. Wie ich bereits sagte, ist es nicht trivial.

SQL erfordert, dass die SELECT-Anweisung jede zurückzugebende Spalte definiert; Die Anzahl und Typen der Spalten dürfen nicht dynamisch sein, wenn die Anweisung ausgeführt wird.

Wenn wir (abgesehen von der ursprünglichen Abfrage) eine weitere Abfrage erstellen, die die Spalten definiert und die erwarteten Zeilen mit Platzhaltern für die Werte zurückgibt, sind wir schon auf halbem Weg. Es bleibt nur noch, eine äußere Verknüpfung mit den von der ursprünglichen Abfrage zurückgegebenen Zeilen durchzuführen und die Spaltenwerte in den entsprechenden Zeilen bedingt zurückzugeben.

Dieser Ansatz funktioniert, wenn Sie einen vordefinierten Satz von Zeilen und Spalten haben, die wir zurückgeben müssen, insbesondere wenn die ursprüngliche Zeilenquelle spärlich ist und wir die "fehlenden" Zeilen generieren müssen. (Wenn Sie beispielsweise die Anzahl der bestellten Produkte abrufen und viele Zeilen fehlen, gibt es keine gute Möglichkeit, die fehlenden Zeilen zu generieren.

Zum Beispiel:

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

Das wird zurückgeben:

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

Damit haben wir alle definierten Spalten und alle Zeilen, die wir zurückgeben möchten. Die erste Spalte wird ausgefüllt. Diese Abfrage benötigt das GROUP BY noch nicht wirklich, aber wir werden es brauchen, sobald wir es mit den Zeilen aus der "echten" Quellergebnismenge abgleichen.

Der "Trick" besteht nun darin, die Zeilen aus unserer Quelle abzugleichen und den Wert aus einer Spalte basierend auf den entsprechenden Bedingungen zurückzugeben.

Was wir generieren werden, ist im Wesentlichen eine Ergebnismenge, die wie folgt aussieht:

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

Dann "reduzieren" wir die Zeilen, indem wir die foo-Spalte aus der Ergebnismenge entfernen und ein GROUP BY auf bar ausführen . Wir werden eine Aggregatfunktion (entweder MAX oder SUM) verwenden, die den Umgang mit NULL-Werten ausnutzt, um ein Ergebnis wie dieses zu erzeugen:

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

Verwenden Sie dieses ziemlich unhandliche SQL:

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

Beachten Sie, dass mysource in der obigen Abfrage kann durch eine Inline-Ansicht ersetzt werden, wobei Klammern um eine geeignete Abfrage gewickelt werden, die die gewünschten Zeilen zurückgibt.

Die Inline-Ansicht mit dem Alias ​​r ist unsere Quelle für die Rückgabe der Zeilen, die wir zurückgeben möchten.

Die Ausdrücke in der SELECT-Liste führen die bedingten Tests durch, um die richtigen Werte für jede Spalte in jeder Zeile auszuwählen.

Angesichts des regelmäßigen Musters der CASE-Anweisungen ist es möglich, etwas SQL zu verwenden, um die Abfrage zu generieren, aber das muss in einem separaten Schritt erfolgen. Die Ausgabe dieser SQL kann verwendet werden, um bei der Erstellung der eigentlichen Abfrage zu helfen, die wir benötigen.

In Ihrem Fall angesichts dieses workdate für die Spaltenüberschrift verwenden möchten, muss diese wahrscheinlich dynamisch generiert werden. (Sie könnten diese zweite Spalte „Wochentag“ aus der ursprünglichen Quellabfrage löschen und in die äußere Abfrage verschieben.

Wenn ich das workdate nicht wüsste Werte für die Überschriften, bevor ich die Abfrage ausführte, dann würde ich mich dafür entscheiden, eine TEMPORARY TABLE zu erstellen und sie mit Ergebnissen aus der ursprünglichen Abfrage zu füllen und dann die temporäre Tabelle abzufragen, um das workdate zu erhalten Kopfzeilen und die "erste Spalte", um die Zeilen zu generieren. Dann würde ich die eigentliche Abfrage für die temporäre Tabelle ausführen.

Um es noch einmal zu wiederholen, ich denke, Sie sollten die Transformation/Pivotierung der Ergebnisse Ihrer ursprünglichen Abfrage besser in PHP durchführen, als es in SQL zu versuchen.