Dies ist ein Fehler in der Projektnormalisierung , der durch die Verwendung einer Unterabfrage in einem case-Ausdruck mit einer nicht deterministischen Funktion angezeigt wird.
Um dies zu erklären, müssen wir im Vorfeld zwei Dinge beachten:
- SQL Server kann Unterabfragen nicht direkt ausführen, daher werden sie immer entrollt oder in eine Anwendung konvertiert .
- Die Semantik von
CASE
ist so, dass ein THEN
Ausdruck nur ausgewertet werden sollte, wenn die WHEN
Klausel true zurückgibt.
Die im Problemfall eingeführte (triviale) Unterabfrage führt daher zu einem Apply-Operator (Nested Loops Join). Um die zweite Anforderung zu erfüllen, platziert SQL Server den Ausdruck zunächst dbo.test6(1) + dbo.test6(2)
auf der Innenseite des Apply:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
... mit der CASE
Semantik, die durch ein Pass-Through- Prädikat für den Join geehrt wird:
[@i]=(1) OR [@i]=(2) OR IsFalseOrNull [@i]=(3)
Die Innenseite der Schleife wird nur ausgewertet, wenn die Durchgangsbedingung den Wert false (Bedeutung @i = 3
) hat. Das ist alles soweit richtig. Der Berechnungsskalar nach dem Join mit verschachtelten Schleifen berücksichtigt auch die CASE
Semantik korrekt:
[Expr1001] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
Das Problem ist, dass die Phase der Projektnormalisierung der Abfragekompilierung Expr1000
unkorreliert ist und feststellt, dass es sicher ist ( Sprecher: nicht ), sie außerhalb der Schleife zu verschieben:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
Dadurch wird die vom Pass-Through- Prädikat * implementierte Semantik gebrochen, sodass die Funktion ausgewertet wird, wenn dies nicht der Fall sein sollte, und eine Endlosschleife entsteht.
Sie sollten diesen Fehler melden. Eine Problemumgehung besteht darin, zu verhindern, dass der Ausdruck aus der Anwendung verschoben wird, indem er korreliert wird (dh @i
in den Ausdruck einbezogen wird), aber dies ist natürlich ein Hack. Es gibt eine Möglichkeit, die Projektnormalisierung zu deaktivieren, aber ich wurde zuvor gebeten, sie nicht öffentlich zu teilen, sodass ich dies nicht tun werde.
Dieses Problem tritt in SQL Server 2019 nicht auf, wenn die Skalarfunktion eingebunden ist , da die eingebundene Logik direkt in der analysierten Struktur ausgeführt wird (lange vor der Projektnormalisierung). Die einfache Logik in der Frage kann durch die Inlining-Logik zum nicht-rekursiven vereinfacht werden:
[Expr1019] = (Scalar Operator((1)))
[Expr1045] = Scalar Operator(CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(int,[Expr1019],0)+(2),0))
... was 3 ergibt.
Ein anderer Weg, um das Kernproblem zu veranschaulichen, ist:
-- Not schema bound to make it non-det
CREATE OR ALTER FUNCTION dbo.Error()
RETURNS integer
-- WITH INLINE = OFF -- SQL Server 2019 only
AS
BEGIN
RETURN 1/0;
END;
GO
DECLARE @i integer = 1;
SELECT
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN (SELECT dbo.Error()) -- 'subquery'
ELSE NULL
END;
Reproduziert auf den neuesten Builds aller Versionen von 2008 R2 bis 2019 CTP 3.0.
Ein weiteres Beispiel (ohne Skalarfunktion) von Martin Smith :
SELECT IIF(@@TRANCOUNT >= 0, 1, (SELECT CRYPT_GEN_RANDOM(4)/ 0))
Dies hat alle wichtigen Elemente benötigt:
CASE
(intern implementiert als ScaOp_IIF
)
- Eine nicht deterministische Funktion (
CRYPT_GEN_RANDOM
)
- Eine Unterabfrage für den Zweig, die nicht ausgeführt werden soll (
(SELECT ...)
)
* Streng genommen könnte die obige Transformation immer noch korrekt sein, wenn die Bewertung von Expr1000
korrekt zurückgestellt wurde, da nur auf die sichere Konstruktion verwiesen wird:
[Expr1002] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
... aber dies erfordert ein internes ForceOrder- Flag (kein Abfragehinweis ), das auch nicht gesetzt ist. In jedem Fall ist die Implementierung der durch die Projektnormalisierung angewendeten Logik falsch oder unvollständig.
Fehlerbericht auf der Azure Feedback-Website für SQL Server.