Letzte Woche bin ich bei der Arbeit mal wieder auf ein interessantes und unerwartetes Verhalten gestoßen. Die stark vereinfachte Beispiel-Query sieht so aus:
SELECT 10 AS abc, 20 AS abc, 30 AS abc_1 FROM dual;
Das ganze ist gedacht für Oracle 11g, andere DBMS habe ich noch nicht getestet. Für Nutzer anderer DBMS zum Verständnis: „dual“ ist in Oracle eine Dummy-Tabelle, die man benutzen kann, um Queries zu bauen, die ohne eigene Tabellen auskommen, z.B. zum Demonstrieren von Datumsfunktionen.
Instinktiv hätte ich jedenfalls erwartet, dass Oracle sich über den doppelten Spaltennamen beschwert und die Query mit einem Fehler abbricht. Macht Oracle aber nicht, stattdessen läuft die Query einwandfrei durch. Was für ein Ergebnis man nun bekommt, hängt davon ab wie man die Query ausgeführt hat.
Im SQL Developer sieht das Ergebnis so aus:
Man bekommt also alle drei Spalten angezeigt, wobei „20″ und „30″ den gleichen Namen haben. Letzteres liegt daran, dass der SQL Developer hier einen Mechanismus verbaut hat, der an doppelte Spaltennamen ein „_1″ anhängt. Es wird also die Spalte „abc“ ausgegeben, bei der zweiten Spalte „abc“ hängt der SQL Developer das Suffix „_1″ an und bei der dritten Spalte scheint dieser Mechanismus dann zu versagen, denn gemeiner- und nicht zufälligerweise hatte ich als Alias bereits „abc_1″ vorgegeben in der Query.
Das ist in einem optischen Tool alles schön und gut. Aber wie sieht es nun mit der programmatischen Verarbeitung der Ergebnisse über PHP aus? Die folgenden Queries wurden unter Verwendung der oci8-Extension abgesetzt. Andere Arten der DB-Abfrage können ggf. andere Ergebnisse zeigen. Das Ergebnis hängt nun interessanterweise davon ab, mit welchen Flags man die Query absetzt. Hier erst mal der PHP-Code zum Ausprobieren:
$sql = "SELECT 10 AS abc, 20 AS abc, 30 AS abc_1 FROM dual"; $stmt = oci_parse( $this->connectionID, $sql ); $res = oci_execute( $stmt ); $ds = oci_fetch_array( $stmt, OCI_NUM ); print_r( $ds );
Spannend wird es nun, wenn man das Ergebnis der Query mit den verwendeten Flags für den oci_fetch_array-Aufruf vergleicht. Mit OCI_NUM werden die Ergebnisse im Array mit numerischen Keys versehen.
Array
(
[0] => 10
[1] => 20
[2] => 30
)
Hier kriegt man ohne Probleme alle drei Spalten. Wenn man aber, im Glauben sinnvolle Aliase vergeben zu haben, sich das Ergebnis über OCI_ASSOC lieber in einem assoziativen Array ausgeben lässt, kriegt man nur noch zwei Ergebnisse:
Array
(
[ABC] => 20
[ABC_1] => 30
)
Spannend: Die OCI-Extension hält sich im Gegensatz zum SQL Developer eher an die in der Query gegebenen Aliase. Die „20″-Spalte überschreibt wegen des gleichen Alias die „10″-Spalte, die „30″-Spalte kommt mit dem gewünschten Alias dahinter.
Wenn einem die Flags egal sind und man sie nicht angibt (oder man OCI_NUM | OCI_ASSOC reinreicht), dann kriegt man per Default sowohl numerische als auch assoziative Keys, was dann wirklich kein sinnvolles Ergebnis mehr ist:
Array
(
[0] => 10
[ABC] => 20
[1] => 20
[2] => 30
[ABC_1] => 30
)
Man könnte jetzt natürlich fragen, wieso man so eine sinnlose Query bauen sollte. Der echte Anwendungsfall ist eine ganze Ecke komplizierter und beinhaltet viele Tabellen, deren Spalten dynamisch über eine GUI benannt werden können, und für deren Interaktionen die Queries natürlich auch dynamisch zusammengebaut werden müssen. In unserem konkreten Fall stellte sich nach genauerem Hinschauen heraus, dass unsere Query keine doppelten Aliase enthielt. Ich fand es trotzdem spannend, diesen Fall mal schnell zu untersuchen. Wer mag kann ja gerne die Query in ähnlicher Form mal unter MySQL etc. laufen lassen und einen Kommentar hinterlassen, was dabei rauskommt.