10 Anzeichen warum Dein T-SQL eine Code Review nicht besteht

Ein sehr provokanter Titel für unseren heutigen Artikel, in dem wir auf einige Alarm-Signale in der T-SQL Entwicklung eingehen möchten. Provokant deshalb, weil es sehr schwer ist absolute Regeln in der Entwicklung festzulegen. Nichtsdestotrotz gibt es gewisse Features und Funktionen, die in einer Code Review unsere Alarm-Glocken klingeln lassen und in den meisten Fällen durch bessere Alternativen ersetzt werden können. Im folgenden Abschnitt wollen wir auf 10 dieser Warnsignale etwas genauer eingehen:

  1. Joins mit benutzerdefinierten Tabellenwertfunktionen: Handelt es sich um ein Multi-Statement Funktion, wird die unterliegende Workload nicht im Ausführungsplan aufgedeckt. Single-Threaded wird im schlimmsten Fall eine Zeile nach der Anderen abgefrühstückt. Abhilfe kann hierbei ein Blick in den Abfrageplan verschaffen, um zu identifizieren, ob es sich um ein Multi- oder Inline-Statement handelt. Was man an dieser Stelle auf keinen Fall vorfinden möchte: geschachtelte Aufrufe weiterer Funktionen.

  2. Joins mit Tabellen-Variablen: Obwohl mit SQL Server 2019 die Genauigkeit der Zeilenschätzung deutlich verbessert wurde, werden dennoch nach wie vor keine Statistiken über den Inhalt von Tabellen-Variablen erzeugt. Dazu kommt, dass INSERT-Operationen nur Single-Threaded abgehandelt werden. Begegnet man einer solchen Tabellen-Variablen in freier Wildbahn, liegt das leider oft daran, dass der Entwickler sich nicht wirklich über die Stärken und Schwächen dieser Funktionalität bewusst ist. Besonders sinnvoll können sie beispielsweise eingesetzt werden, um Kompiliervorgänge zu reduzieren. Allgemein werden sie für uns jedoch eher weitere Fragen auf und bringen uns dazu noch etwas genauer hinzuschauen.

  3. CROSS JOIN: Natürlich gibt es immer wieder Fälle, in denen jede einzelne Zeile einer Tabelle ohne jeglichen Filter benötigt wird – wenn wir ehrlich sind sollten diese aber sehr, sehr unüblich sein. Auch hier gilt: Hören wir diese beiden Worte, schauen wir lieber nochmal genau hin und gehen dem Problem auf den Grund.

  4. Mehrere Joins auf dem selben CTE: CTEs sind eine tolle Sache, auch wenn eine temporäre Tabelle oft einen geeigneterer Kandidat ist. Wann es in einer Code Review aber wirklich spannend wird: Wenn die gleiche CTE mehrfach referenziert wird. Beispiel: FROM cte JOIN cte JOIN cte, am Besten noch mit jeweils unterschiedlichen Aliasen. Das Problem dabei wird erst über den Ausführungsplan deutlich. Die CTE wird mehrere Male im Plan aufgeführt und wird wiederholt die unterliegenden Tabellen beanspruchen.

  5. Das “Spülbecken” Design-Pattern: Die folgende beispielhafte Klausel kann der SQL Server nur sehr schwer optimieren: WHERE (CustomerId = @Customer OR @CustomerId IS NULL). Was wir tun können – das Problem selbst in die Hand nehmen. Mit dynamischem SQL oder RECOMPILE-Hinweisen können wir Abhilfe schaffen und die Situation von vorne herein umgehen.

  6. SELECT … INTO #TempTable: Temporäre Tabellen sind eine tolle Sache. Ihr Ruf ist oft weit schlechter als ihr eigentlicher Nutzen. Ein SELECT INTO kann sogar Vorteile gegenüber einer expliziten Tabelle besitzen. Dennoch ist der Grund für die Verwendung einer solchen Anweisung in produktivem Code oft auf unsaubere Arbeitsweise zurückzuführen. In den meisten Fällen ist es allerdings ratsam sich die Zeit zu nehmen ein CREATE TABLE Statement zu schreiben und die entsprechend benötigten Datentypen explizit zu bestimmen. Ein Parade-Beispiel zur Problematik: In einer Code Review hatte ein Entwickler eine vollständige Tabelle in die TempDB geladen, um anschließend die gewünschten Zeilen von hier zu filtern. Seine Intention war es, die Benutzer-Datenbank zu entlasten. Gute Intention – problematische Umsetzung.

  7. Indexe für temporäre Tabellen: Indexe für temporäre Tabellen klingen erstmal nicht schlecht. Wer will schon nicht von einem Index profitieren? Aber auch hier liegt der Teufel leider wieder im Detail. Rückfragen werden in solchen Fällen oft mit Antworten wie “Ich dachte ein Index könnte vielleicht helfen” beantwortet. Ein klarer Hinweis drauf, dass das betroffene Problem weiter analysiert und optimiert werden sollte. Auch sollte nicht vergessen werden, dass durch die Verwendung eines Indexes für temporäre Tabellen Auswirkung auf das Caching der temporären Tabellen auswirkt!

  8. WITH (NOLOCK): Auch wenn es vielleicht schwer zu glauben ist, wird immer wieder von unterschiedlichen Quellen demonstriert, dass die Verwendung von WITH (NOLOCK) für zufällige Ergebnisse sorgen kann. Natürlich gilt auch hier, dass es Situationen gibt, in denen keine Probleme entstehen. In den meisten Fällen liegt es jedoch an der Unwissenheit des Entwicklers über den Effekt den diese Anweisung haben kann. Besonders dramatisch: Die Verwendung in einem UPDATE-Statement. Viel Spaß beim Debugging!

  9. BEGIN TRANSACTION & COMMIT ohne Fehlerbehandlung: Der Grund für die Verwendung einer Transaktion sollte klar sein. Widersprüchlich wird es erst dann, wenn die Fehlerbehandlung nur halbherzig oder sogar komplett vernachlässigt wird. Wenn eine Aktion kritisch genug ist, dass eine Transaktion dafür definiert wird, sollte auch der Prozess nach der unerfolgreichen Ausführung dieser Transaktion klar definiert sein.

  10. Kommentare und Dokumentation: In vielen Entwicklerteams gilt Code erst als vollständig, wenn entsprechend sinnvolle Kommentare und Dokumentation vorhanden ist. Kommentare und Dokumentation sparen Ihnen, Ihrem Team und allen Nachfolgern eine enorme Menge Zeit und Nerven. Man wird es Ihnen danken!

Natürlich haben wir immer wieder Code Reviews, in denen wir Code abnehmen und zulassen der mehr als nur einen der erwähnten Punkte beinhaltet. Dennoch sorgen sie alle dafür, dass wir noch etwas genauer hinschauen und gegebenenfalls Fragen gestellt werden. Und genau diese Fragen sollten Sie sich vermutlich vor der nächsten Code Review auch stellen!