Heute habe ich an einer SQL-„Tuning“-Schulung teilgenommen. Ich hatte im Vorfeld etwas Befürchtungen, dass die Themen zu abgehoben sein könnten für meine bisherigen SQL-Kenntnisse. Die befinden sich nämlich in einem Bereich, den ich vielleicht als „solide Grundlagen“ bezeichnen würde, wobei ich mir bei „solide“ nun nicht mehr ganz so sicher bin 😀

Meine Befürchtungen haben sich aber nicht bestätigt, im Gegenteil: Ich fand es total interessant und der Dozent hat es auch geschafft, uns alle immer wieder mit ins Boot zu holen und bei Lücken in den Grundlagen diese schnell aufzufüllen. Die Methoden, die er vorgestellt hat um Abfragen performanter zu gestalten waren alles andere als abgehoben und jetzt habe ich direkt Bock bekommen, mehr mit SQL zu machen um dieses neue Wissen anzuwenden.

Themen waren z.B. die GROUP-Funktionen, analytische Funktionen, PL/SQL-Abfragen und die Benutzung des DATE-Typs, alles hauptsächlich auf die Datenbank Oracle bezogen.

Ein kleines Beispiel möchte ich herausgreifen und hier darstellen. Und zwar Abfragen, bei denen man SQL und PL/SQL-Anweisungen vermischt. PL/SQL ist quasi die Programmiersprache in Oracle, über die z.B. Funktionen oder andere dort programmierte Abläufe in SQL-Abfragen genutzt werden können. Nagelt mich da bitte nicht auf alle Details fest, ich schreib das hier genauso runter wie ich es mir gemerkt und aufgeschrieben habe.

Schauen wir uns eine einfache Abfrage an:

select * 
 from tab_namen 
 where nachname = v(last_nachname);

Hier wird in einer Tabelle tab_namen nach Einträgen gesucht, bei denen in der Spalte nachname der Wert v(last_nachname) drin steht. Dieses wiederum ist eine Prozedur, die zuvor in PL/SQL definiert wurde und den Wert des zuletzt angeforderten Nachnamens zurückliefert.

Die Datenbank geht bei diesen Abfragen nun so vor, dass sie sich die Tabelle tab_namen schnappt, dort jeden Eintrag durchgeht und die Spalte nachname mit dem Wert aus v(last_nachname) vergleicht. Dabei ruft sie für JEDE Zeile in der Tabelle die Prozedur v(last_nachname) auf. Das tut sie deshalb, weil sie an dieser Stelle nicht weiß, dass der Wert immer der gleiche bleibt.

Anders sieht es jedoch aus, wenn man die Prozedur in einem Subselect „versteckt“, und darüber den Vergleich macht:

select *
   from tab_namen
   where nachname = (
     select v(last_nachname) 
      from dual);

Die Tabelle dual ist hierbei nur eine Standard Dummy-Tabelle in SQL mit genau einem Eintrag, die z.B. benutzt wird, wenn man nicht an den Daten aus einer Tabelle sondern nur an denen aus etwa Systemfunktionen interessiert ist. In diesem Fall sind wir an dem Wert v(last_nachname) interessiert, der Rest steht da, weil wir so in Oracle an diesen Wert über ein SELECT-Statement dran kommen. Übrigens auch etwas, was ich heute neu gelernt habe…

Zurück zum Topic: Warum ist diese Schreibweise nun besser? Sieht komplizierter aus und gefühlt ist da ja noch mehr zu tun für die Datenbank.

Joa, sieht vielleicht so aus, ist es aber nicht. Denn die Datenbank schaut sich nun das Subselect einzeln an und erkennt, dass hier immer das gleiche gemacht wird. Das liegt an der internen Verarbeitung der SELECT-Statements in Oracle. Damit die Datenbank die Anfrage versteht, wird diese quasi kompiliert und optimiert. Um das ganze zu beschleunigen, speichert die Datenbank einzelne Abfragen ab, in dem es aus der entsprechenden Zeichenkette eines SELECTs einen Hash generiert. Kommt nun eine weitere Abfrage, wird diese ebenfalls als Hash umgewandelt und die Datenbank schaut, ob dieser schon im Speicher vorhanden ist. In diesem Fall muss im Hintergrund nicht noch einmal kompiliert und optimiert werden.

Ein gutes Beispiel dafür ist diese Abfrage:

Select dbms_random.value random_wert1, 
  (select dbms_random.value from dual) random_wert2 
  from tab_123;

Angenommen, die Tabelle tab_123 beinhaltet 5 Zeilen. Die Funktion dbms_random.value liefert eine beliebige Zahl zwischen 0 und 1 zurück. Die obige Abfrage liefert als Ergebnis z.B. folgendes:

random_wert1random_wert2
0.123456789012340.43872456523768
0.983451748586920.43872456523768
0.337582374753440.43872456523768
0.582364172712220.43872456523768
0.238748237475740.43872456523768

Die Datenbank macht es sich hier einfach und sieht, dass das Subselect (select dbms_random.value from dual) immer die gleiche Zeichenkette ist und führt diese nur EINMAL aus, während die Random-Funktion für Wert1 jedes Mal neu ausgeführt wird.

Warum ist das jetzt im oberen Beispiel bei PL/SQL besonders interessant? Nun, hier findet jedesmal ein Umgebungswechsel statt zwischen SQL und PL/SQL. Und der kostet. Je weniger ich das mache, desto besser. Und wenn es irgendwann um seeeehr viele Daten geht, merkt man solche Unterschiede deutlich.

Ich find’s super interessant, mal sehen ob ich das neue Wissen demnächst auch direkt einsetzen kann!

Bis dahin, wieder was gelernt 🙂

One Reply to “… dass ein simples SELECT mehr manchmal ausreicht, um eine SQL-QUERY deutlich performanter zu gestalten”

Schreibe einen Kommentar