it-swarm.com.de

Warum sind Verbindungszweige in einem CASE-Ausdruck auf 10 Zweige beschränkt?

Warum macht dieser CASE Ausdruck:

SELECT CASE column 
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        ... c -> i
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END [col] 
FROM LinkedServer.database.dbo.table

Dieses Ergebnis produzieren?

Fehlermeldung: Nachricht 8180, Ebene 16, Status 1, Zeile 1
Erklärung (en) konnten nicht erstellt werden.
Nachricht 125, Ebene 15, Zustand 4, Zeile 1
Fallausdrücke dürfen nur bis Stufe 10 verschachtelt werden.

Offensichtlich gibt es hier keinen verschachtelten CASE Ausdruck, obwohl es mehr als 10 "Zweige" gibt.

Eine weitere Kuriosität. Diese Inline-Tabellenwertfunktion erzeugt den gleichen Fehler:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
     @var varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table
)

Aber ein ähnliches TVF mit mehreren Aussagen funktioniert gut:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
    @var varchar(20)
)
RETURNS @result TABLE 
(
    value varchar(max)
)
AS
BEGIN
    INSERT INTO @result
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table

RETURN;
END
19
Andrey

Offensichtlich gibt es hier keinen verschachtelten CASE Ausdruck.

Nicht im Abfragetext, nein. Der Parser erweitert jedoch immer CASE Ausdrücke in die verschachtelte Form:

SELECT CASE SUBSTRING(p.Name, 1, 1)
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM AdventureWorks2012.Production.Product AS p

Local query plan

Diese Abfrage ist lokal (kein Verbindungsserver) und der Compute Scalar definiert den folgenden Ausdruck:

Nested CASE expression

Dies ist in Ordnung, wenn es lokal ausgeführt wird, da Parser keine verschachtelte CASE -Anweisung mit einer Tiefe von mehr als 10 Ebenen sieht (obwohl sie eine an die späteren Phasen der lokalen Abfragekompilierung weiterleitet).

Bei einem Verbindungsserver wird der generierte Text möglicherweise zur Kompilierung an den Remote-Server gesendet. Wenn dies der Fall ist, sieht der Remote-Parser eine verschachtelte CASE -Anweisung, die mehr als 10 Ebenen tief ist, und Sie erhalten den Fehler 8180.

Eine weitere Kuriosität. Diese Inline-Tabellenwertfunktion erzeugt den gleichen Fehler

Die Inline-Funktion wird direkt in den ursprünglichen Abfragetext erweitert, sodass es nicht verwunderlich ist, dass beim Verbindungsserver derselbe Fehler auftritt.

Aber ein ähnliches TVF mit mehreren Anweisungen funktioniert einwandfrei

Ähnlich, aber nicht gleich. Die msTVF beinhaltet eine implizite Konvertierung in varchar(max), wodurch verhindert wird, dass der Ausdruck CASE an den Remote-Server gesendet wird. Da CASE lokal ausgewertet wird, sieht ein Parser niemals ein überverschachteltes CASE und es liegt kein Fehler vor. Wenn Sie die Tabellendefinition von varchar(max) in den impliziten Typ des Ergebnisses CASE - varchar(2) ändern, wird der Ausdruck mit der msTVF entfernt und Sie erhalten eine Fehlermeldung.

Letztendlich tritt der Fehler auf, wenn ein überverschachteltes CASE vom Remote-Server ausgewertet wird. Wenn CASE im Remote Query-Iterator nicht ausgewertet wird, tritt kein Fehler auf. Das Folgende enthält beispielsweise ein CONVERT, das nicht remote ist, sodass kein Fehler auftritt, obwohl ein Verbindungsserver verwendet wird:

SELECT CASE CONVERT(varchar(max), SUBSTRING(p.Name, 1, 1))
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

CASE not remoted

24
Paul White 9

Meine Vermutung ist, dass die Abfrage irgendwo auf dem Weg neu geschrieben wird, um eine etwas andere CASE -Struktur zu haben, z.

CASE WHEN column = 'a' THEN '1' ELSE CASE WHEN column = 'b' THEN '2' ELSE ...

Ich glaube, dies ist ein Fehler bei jedem von Ihnen verwendeten Verbindungsserver-Anbieter (vielleicht sogar bei allen - ich habe gesehen, dass er gegen mehrere gemeldet wurde). Ich glaube auch, dass Sie nicht den Atem anhalten sollten, um auf eine Korrektur zu warten, weder in der Funktionalität noch in der verwirrenden Fehlermeldung, die das Verhalten erklärt - dies wurde lange Zeit gemeldet und betrifft Verbindungsserver (die seit SQL nicht viel Liebe hatten) Server 2000) und betrifft weit weniger Menschen als diese verwirrende Fehlermeldung , die nach derselben Langlebigkeit noch behoben werden muss.

Wie Paul weist darauf hin , erweitert SQL Server Ihren Ausdruck CASE auf die verschachtelte Variante, und der Verbindungsserver mag ihn nicht. Die Fehlermeldung ist verwirrend, aber nur, weil die zugrunde liegende Konvertierung des Ausdrucks nicht sofort sichtbar (oder in keiner Weise intuitiv) ist.

Eine Problemumgehung (abgesehen von der Funktionsänderung, die Sie Ihrer Frage hinzugefügt haben) besteht darin, eine Ansicht oder eine gespeicherte Prozedur auf dem Verbindungsserver zu erstellen und darauf zu verweisen, anstatt die vollständige Abfrage über den Verbindungsserveranbieter weiterzuleiten.

Eine andere (vorausgesetzt, Ihre Abfrage ist wirklich so simpel und Sie möchten nur den numerischen Koeffizienten der Buchstaben a bis z) lautet:

SELECT [col] = RTRIM(ASCII([column])-96)
FROM LinkedServer.database.dbo.table;

Wenn Sie dies unbedingt benötigen, um so zu funktionieren, wie es ist, schlage ich vor, dass Sie sich direkt an den Support wenden und einen Fall öffnen, obwohl ich nicht für die Ergebnisse bürgen kann. Möglicherweise erhalten Sie nur Problemumgehungen, auf die Sie auf dieser Seite bereits Zugriff haben.

6
Aaron Bertrand

sie können dies umgehen, indem Sie

SELECT COALESCE(
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'a' THEN '1' 
    WHEN 'b' THEN '2' 
    WHEN 'c' THEN '3' 
    WHEN 'd' THEN '4' 
    WHEN 'e' THEN '5' 
    WHEN 'f' THEN '6' 
    WHEN 'g' THEN '7' 
    WHEN 'h' THEN '8' 
    WHEN 'i' THEN '9' 
    ELSE NULL
END,
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'j' THEN '10' 
    WHEN 'k' THEN '11'  
END)
FROM SQL2K8R2.AdventureWorks.Production.Product AS p
5
Nik

Eine andere Problemumgehung für dieses Problem besteht darin, eine satzbasierte Logik zu verwenden, bei der der Ausdruck CASE durch einen linken Join (oder eine äußere Anwendung) für eine Referenztabelle (ref im folgenden Code) ersetzt wird permanente, temporäre oder abgeleitete Tabelle/CTE. Wenn dies in mehreren Abfragen und Prozeduren erforderlich ist, würde ich dies lieber als permanente Tabelle haben:

SELECT ref.result_column AS [col] 
FROM LinkedServer.database.dbo.table AS t
  LEFT JOIN
    ( VALUES ('a',  '1'),
             ('b',  '2'), 
             ('c',  '3'),
             ---
             ('j', '10'),
             ('k', '11')
    ) AS ref (check_col, result_column) 
    ON ref.check_col = t.column ;
2
ypercubeᵀᴹ