Advanced T-SQL: Vorsicht mit Short-Circuit Evaluation

17. Februar 2015
Kurzschlussauswertungen, welche in IT-Fachkreisen häufig auch als bedingte Auswertungen bezeichnet und im Englischen mit dem Begriff Short-circuit evaluation übersetzt werden, beschreiben in der Informatik Strategien zur Auswertung boolescher Formeln. Mit Hilfe von Kurschlussauswertungen können Analyseprozesse langer und komplexer boolescher Terme vorzeitig abgebrochen werden, sofern das Endergebnis des auszuwertenden Gesamtausdrucks bereits vor Ermittlung der letzten atomaren Bedingung feststeht.

 

Meine Artikelserie zum Thema “Advanced T-SQL” umfasste bislang diese Beiträge:

Im Rahmen meines nun folgenden Artikels wird auf Kurzschlussauswertungen in Verbindung mit T-SQL und dem Microsoft SQL-Server eingegangen. Dabei werden insbesondere auch einige mögliche Stolperfallen bei der Konvertierung numerischer Werte mit den SQL-Datentypen VARCHAR und NVARCHAR aufgezeigt.

 

Short-Circuit Evaluation in modernen Programmiersprachen

Durch das Konzept der Kurschlussauswertung kann oftmals einen Gewinn hinsichtlich der Performance erzielt werden. Insbesondere Compiler moderner objektorientierter Programmiersprachen wie C# oder Java verwenden daher Kurzschlussauswertungen als Mittel zur Optimierung. Anhand des folgendes C#-Snippets wird dies exemplarisch belegt:

   1: private static bool ComplexCheckMethod()

   2: {

   3:     // Some C#-Code

   4:     return [CheckResult];

   5: }

   6:  

   7: static void Main(string[] args)

   8: {

   9:     if (false && ComplexCheckMethod())

  10:     {

  11:         ...

  12:     }

  13: }

Da die Bedingung innerhalb der if-Anweisung (Zeile 9) niemals wahr werden bzw. den Wert “true” annehmen kann, wird das Ergebnis der zuvor implementierten ComplexCheckMethod-Funktion an dieser Stelle nicht benötigt und der Quellcode innerhalb der Funktion somit gar nicht erst ausgeführt. Moderne Entwickler-Tools wie beispielsweise der ReSahrper der Firma JetBranis erkennen dies und weisen den Entwickler darauf hin.

 

Short-Circuit Evaluation in T-SQL

Mit T-SQL innerhalb des SQL-Server verhält es sich bezüglich Kurzschlussauswertungen (leider) etwas anders. Hier hängt der Einsatz einer möglichen Short-Circuit Evaluation vom eingesetzten Datentypen innerhalb des T-SQL-Statements ab. Wird beispielsweise diese Tabellenstruktur

   1: CREATE TABLE TblShortCircuitEvaluation

   2: (

   3:     VarCharColumn VARCHAR(1000),

   4:     NVarCharColumn NVARCHAR(1000),

   5: );

mit folgendem Inhalt verwendet

image

so liefert eine Abfrage mit Filter auf die VARCHAR(1000)-Spalte

SELECT * 

FROM TblShortCircuitEvaluation

WHERE ISNUMERIC(VarCharColumn) = 1 AND (CAST(VarCharColumn AS INT) = 42);

ein gültiges Abfrageergebnis, nämlich:

image

Wird dagegen die gleiche Filterbedingung auf die NVARCHAR(1000)-Spalte angewendet, also folgendes Statement abgesetzt

SELECT * 

FROM TblShortCircuitEvaluation

WHERE ISNUMERIC(NVarCharColumn) = 1 AND (CAST(NVarCharColumn AS INT) = 42);

, so wird statt eines Abfrageergebnisses ein Konvertierungsfehler zurückgegeben:

Msg 245, Level 16, State 1, Line 1

Conversion failed when converting the nvarchar value 'TextValue' to data type int.

In zweiten Fall, also bei Verwendung der NVARCHAR(1000)-Spalte, wird für alle Datensätze innerhalb der Datenbanktabelle, auch wenn die erste Bedingung ISNUMERIC(NVarCharColumn)=1 keinen TRUE-Wert zurückliefert, die anschließende Konvertierung versucht durchzuführen und die zweite Filterbedingung ausgewertet. Hierbei entsteht nun der oben aufgeführte der Konvertierungsfehler.

Warum dieses Verhalten genau so an den Tag gelegt wird, ist leider nicht klar ersichtlich. Im Internet existieren zwar zahlreiche Foren und Portale, die sich mit dem Thema Kurzschlussauswertung und SQL auseinander setzen, aber eine genaue Erklärung dafür kann nicht gefunden werden. Selbst dem standardisierten ANSI-SQL ist nur folgendes Zitat zu entlocken:

"Where the precedence is not determined by the Formats or by parentheses, effective evaluation of expressions is generally performed from left to right. However, it is implementation-dependent whether expressions are actually evaluated left to right, particularly when operands or operators might cause conditions to be raised or if the results of the expressions can be determined without completely evaluating all parts of the expression." (Kapitel 6.3.3.3 Rule evaluation order, Seite 41 ff)

Auf gut Deutsch: Im SQL wird die abgekürzte boolesche Evaluation leider nicht automatisch verwendet. Die Datenbank-Implementation kann sie gebrauchen, muss aber nicht Zwinkerndes Smiley.   

Damit die obere Abfrage mit Filter auf der NVARCHAR(1000)-Spalte auch fehlerfrei ausgewertet und ein gültiges Ergebnis zurückliefert, empfiehlt es sich eine kleine Sicherheit im SQL-Statement einzubauen. Diese könnte beispielsweise wie folgt aussehen:

SELECT * 

FROM TblShortCircuitEvaluation

WHERE CAST((CASE WHEN ISNUMERIC(NVarCharColumn) = 1 THEN NVarCharColumn END) AS INT) = 42;

Hier wird nun ein gültiges Abfrageergebnis zurückgeliefert, nämlich:

image

Fazit

Im diesem Artikel wurde gezeigt, dass leider weder mit T-SQL innerhalb des Microsoft SQL-Server noch mit standardisierten ANSI-SQL abgekürzte boolesche Evaluationen mittels Kurschlussauswertung wirklich garantiert werden können. Um auf Nummer sicher zu gehen, empfiehlt es sich sogar immer von einer Prüfung und Auswertung alle Terme bzw. sämtliche einzelne Bedingungen eines SQL-Statements auszugehen.

Des Weiteren sollten auch in Anbetracht von Kurzschlussauswertung Änderungen von SQL-Datentypen immer kritisch hinterfragt werden. Selbst anfangs unbedenklich erscheinende  Aussagen wie "Wir ändern mal eben schnell den Datentyp von VARCHAR auf NVARCHAR" können durchaus gefährlich sein und unerwünschte Auswirkungen auf bestehende SQL-Statements haben. Die Code-Beispiele in diesem Artikel zeigen dies deutlich und es gilt auch hier das bekannte Sprichwort: der Teufel steckt im Detail Teufel.

 

Quellen:

  1. char und varchar (Transact-SQL) (Letzter Zugriff: 2014-12-12)
  2. nchar und nvarchar (Transact-SQL) (Letzter Zugriff: 2014-12-12)