2012-08-10 5 views
5

Ich bin ein SELECT zu tun, die CASE nvarchar Werte in eine richtige Art zu konvertieren verwendet, etwa wie folgt:CASE THEN-Klausel immer ausgewertet

SELECT CASE 
    WHEN @propType = 'money' THEN convert(money, datavalue) 
    [...] 
    ELSE datavalue 
END 
FROM [...] 

Es scheint jedoch, die convert immer dann ausgeführt wird, auch wenn @propType ist nicht gleich dem Geld. Runnable Beispiel:

declare @proptype nvarchar(50)= 'nvarchar' 
declare @val nvarchar(10) = 'test' 
select 
    case @proptype 
     when 'money' then convert(money, @val) 
     else @val 
    end 

Warum ist das, und wie kann ich es umgehen? Die MSDN-Dokumentation sagt dies:

Die CASE-Anweisung seine Bedingungen nacheinander auswertet und stoppt mit der ersten Bedingung, deren Bedingung erfüllt ist. In einigen Fällen wird ein Ausdruck ausgewertet, bevor eine CASE-Anweisung die Ergebnisse des Ausdrucks als Eingabe empfängt. Fehler in Auswertung dieser Ausdrücke sind möglich. Aggregierte Ausdrücke, die erscheinen, wenn WHEN Argumente zu einer CASE-Anweisung zuerst ausgewertet werden, dann , die der CASE-Anweisung bereitgestellt werden. Zum Beispiel erzeugt die folgende Abfrage einen Fehler durch Division durch Null, wenn der Wert des MAX Aggregats erzeugt wird. Dies geschieht vor dem Auswerten des CASE-Ausdrucks.

Ich bin mir nicht sicher, ob das relevant ist, aber die Sprache ist etwas schwer für einen Nicht-Muttersprachler, also vielleicht ist es?

+0

Der einzige Zweck ausdrücklich ein 'varchar' zu' money' Umwandlung, wenn es implizit zurück in 'varchar' umgewandelt wird, afaik für Zwecke der Formatierung warum also entweder speichern Sie es nicht in der erforderlichen Formatierung zu beginnen mit oder lassen Sie den Client die Formatierung behandeln? –

+1

Die Quelldaten unterliegen nicht meiner Kontrolle. Im konkreten Fall muss ich ziemlich viele Sprünge durchlaufen, um die Daten in ein solches Format zu bringen, dass ich sie in die Berichtstabelle einfügen kann, für die sie bestimmt ist (die richtig getippt ist) – carlpett

Antwort

3

Werfen Sie einen Blick auf die folgende Use caution when Using CONVERT() with CASE or IF functions in Transact SQL (T-SQL)

Die ersten Gedanken sind in der Regel eine der folgenden „Da der erste ausgewertet Wert numerisch ist, es umgewandelt wird in Dezimalzahlen, und alle anderen Daten zu erwarten sei auch eine Dezimalzahl "ODER" Wenn SQL Server in der Lage ist, ANY der Werte in den angegebenen Typ zu konvertieren, dann werden alle Werte voraussichtlich vom konvertierten Typ sein ". Das ist jedoch nicht korrekt (obwohl der zweite ist in der Nähe)!

Das eigentliche Problem ist, dass, wenn Sie die Werte überall Konvertieren wählen innerhalb der Case-Anweisung, der Datentyp die Werte konvertiert auf die erwartete Art des Wertes ALL unabhängig davon, ob sie von diese Art oder nicht. Selbst wenn KEINE der Werte tatsächlich konvertiert werden können (selbst wenn die Codezeile "Convert" nie ausgeführt wird), werden ALLE Werte der Werte weiterhin vom Typ erwartet, der von der Convert-Funktion angegeben wird!

+0

Danke, dies beleuchtet das Warum, aber Es gibt wirklich keinen Weg davon weiter? – carlpett

1

Um klar zu sein über das, was die then Klausel ausgewertet nicht wird geschieht, ist.

Sie sehen den gleichen Fehler, wenn Sie

SELECT CASE @proptype 
     WHEN 'money' THEN $1.0 /*<-- Literal of datatype money*/ 
     ELSE @val 
     END 

Die Dokumentation für CASE tun erklärt, dass der Typ Return

die höchste Priorität Typ aus dem Satz von Typen in result_expressions Kosten und die optionale . Weitere Informationen finden Sie unter Informationen Data Type Precedence (Transact-SQL).

money hat eine höhere Priorität als Datentyp nvarchar so die else @val ausgewertet wird dann das zu money gegossen wird und ausfällt.

Eine mögliche Problemumgehung wäre die Umwandlung in sql_variant, da diese einen höheren Datenrang hat als beide.

declare @proptype nvarchar(50)= 'nvarchar' 
declare @val nvarchar(10) = 'test' 
select 
    case @proptype 
     when 'money' then convert(money, @val) 
     else cast(@val as SQL_VARIANT) 
    end