TSQL Fehler bei Case (simple Version)
Die Case-Anweisung von T-SQL verhält sich sehr interessant (fehlerhaft?) bei Ausdrücken die in der einfachen Version des Statements in der Case-Expression angegeben werden.
Die einfache Syntax siehe wie folgt aus:
| T-SQL | | copy code | | ? |
| 1 | CASE input_expression |
| 2 | WHEN when_expression THEN result_expression |
| 3 | [ ...n ] |
| 4 | [ |
| 5 | ELSE else_result_expression |
| 6 | ] |
| 7 | END |
Um das Problem zu demonstrieren, möchte ich einmal das folgende Statement zur Diskussion stellen:
| T-SQL | | copy code | | ? |
| 01 | DECLARE @i INT |
| 02 | |
| 03 | SET @i = 0 |
| 04 | |
| 05 | WHILE @i<10 |
| 06 | BEGIN |
| 07 | PRINT |
| 08 | CASE CONVERT(INT,RAND()*5+1) |
| 09 | WHEN 1 THEN 10 |
| 10 | WHEN 2 THEN 20 |
| 11 | WHEN 3 THEN 30 |
| 12 | WHEN 4 THEN 40 |
| 13 | WHEN 5 THEN 50 |
| 14 | WHEN 6 THEN 60 |
| 15 | ELSE 0 |
| 16 | END |
| 17 | SET @i = @i + 1 |
| 18 | END |
Das CASE-Statement verwendet die Random-Funktion RAND().
Diese Funktion liefert Werte zwischen 0 und 1 zurück. Multipliziert man nun den Rückgabewert mit 5 sollten also bis dahin Zahlen zwischen 0 und 5 zurückgeliefert werden. Um 0-Werte auszuschließen wird abschließend noch 1 dazu addiert und der endgültige Wertebereich liegt zwischen 1 und 6.
Soweit so gut.
Lässt man jedoch die oben beschriebene Schleife laufen, erhält man als Ergebnis aus dem CASE-Austruck auch den ELSE-Wert 0 zurückgeliefert. Dies darf jedoch aufgrund der Berechnung eigentlich gar nicht geschehen!
Das Problem liegt hier in der Art wie die CASE-Funktion die input_expression behandelt. Anscheinend wird der Ausdruck trotz CAST/CONVERT nicht korrekt in seinem Datentyp interpretiert und liefert als Ergebnis wahrscheinlich einen FLOAT-Wert zurück.
(Zur Behandlung von Typkonvertierungen siehe “T-SQL CASE und die implizite Datentypkonvertierung”)
Dieser Wert ist durch die Random-Funktion allerdings nicht im Einzelfall zu bestimmen und kann, wenn überhaupt, nur durch aufwändige Tests ermittelt werden.
Als Regel können wir hier also festhalten:
Wenn eine CASE-Inputexpression ein nicht deterministisches Ergebnis liefert, ist das Verhalten der Funktion auf das genaueste zu testen.
Als Workaround muss man dafür sorgen, dass der Wert für die Inputexpression nicht im CASE berechnet wird, sondern ggf. vorher. Sei es in einer CTE, wenn man den Wert innerhalb einer Abfrage benötigt, oder vorher in einer Variablen.
Ein Beispiel für die Version mit der Variablen findet man nachfolgend:
| T-SQL | | copy code | | ? |
| 01 | DECLARE @i INT |
| 02 | DECLARE @w INT |
| 03 | |
| 04 | SET @i = 0 |
| 05 | |
| 06 | WHILE @i<10 |
| 07 | BEGIN |
| 08 | SET @w = CONVERT(INT,RAND()*5+1) |
| 09 | PRINT |
| 10 | CASE @w |
| 11 | WHEN 1 THEN 10 |
| 12 | WHEN 2 THEN 20 |
| 13 | WHEN 3 THEN 30 |
| 14 | WHEN 4 THEN 40 |
| 15 | WHEN 5 THEN 50 |
| 16 | WHEN 6 THEN 60 |
| 17 | ELSE 0 |
| 18 | END |
| 19 | SET @i = @i + 1 |
| 20 | END |
Wenn jemand einen anderen Weg, oder eine Erklärung für dieses absonderliche Verhalten hat, ist er/sie aufgefordert dies in den Kommentaren der interessierten Öffentlichkeit mitzuteilen.
Facebook
XING
META-SQL