Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Outer Apply Unerwartete Rückgabe von Spalten NOT NULL, wenn keine Übereinstimmung vorhanden ist

Dies ist sicherlich ein Fehler im Produkt.

Ein ähnlicher Fehler wurde bereits gemeldet und als geschlossen "Wird nicht behoben" .

Einschließlich dieser Frage, des verknüpften Verbindungselements und weiter zwei Fragen auf dieser Seite Ich habe vier Fälle dieser Art von Verhalten mit Inline-TVFs und OUTER APPLY gesehen - Alle hatten das Format

OUTER APPLY dbo.SomeFunction(...) F

Und gab korrekte Ergebnisse zurück, wenn es als

geschrieben wurde
OUTER APPLY (SELECT * FROM dbo.SomeFunction(...)) F

Das sieht also nach einer möglichen Problemumgehung aus.

Für die Abfrage

WITH Test AS
(
       SELECT 12 AS PropertyID,
              $350000 AS Ap1,
              350000 AS Ap2
)
SELECT LP.*
FROM Test T
OUTER APPLY dbo.TVFTest
(
       T.PropertyID,
       T.Ap1,
       T.Ap2
) LP;

Der Ausführungsplan sieht folgendermaßen aus

Und die Liste der Ausgabespalten in der endgültigen Projektion ist. Ausdr1000, Ausdr1001, Ausdr1003, Ausdr1004.

In der Konstantentabelle unten rechts sind jedoch nur zwei dieser Spalten definiert.

Die wörtlichen $350000 ist in der Konstantentabelle oben rechts definiert (Expr1001). Diese wird dann mit der äußeren Verknüpfung auf die Konstantentabelle unten rechts verknüpft. Da keine Zeile der Join-Bedingung entspricht, werden die beiden dort definierten Spalten (Expr1003, Expr1004) korrekt als NULL ausgewertet. dann fügt der Compute-Skalar schließlich das Literal 12 hinzu unabhängig vom Ergebnis des Outer Joins als neue Spalte (Expr1000) in den Datenfluss.

Das ist überhaupt nicht die richtige Semantik. Vergleichen Sie mit dem (korrekten) Plan, wenn der Inline-TVF manuell eingefügt wird.

WITH Test
     AS (SELECT 12      AS PropertyID,
                $350000 AS Ap1,
                350000  AS Ap2)
SELECT LP.*
FROM   Test T
       OUTER APPLY (SELECT KeyID,
                           MatchValue1,
                           MatchValue2,
                           CASE
                             WHEN MatchValue1 <> MatchValue2
                               THEN 'Not equal'
                             ELSE 'Something else'
                           END AS MatchTest
                    FROM   (SELECT T.PropertyID AS KeyID,
                                   T.Ap1        AS MatchValue1,
                                   T.Ap2        AS MatchValue2) TestRow
                    WHERE  MatchValue1 <> MatchValue2) LP 

Hier sind die in der endgültigen Projektion verwendeten Spalten Expr1003, Expr1004, Expr1005, Expr1006 . All dies wird im konstanten Scan unten rechts definiert.

Beim TVF scheint schon sehr früh alles schief zu laufen.

Hinzufügen von OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); zeigt der Eingangsbaum zum Prozess schon falsch. In SQL ausgedrückt ist es so etwas wie.

SELECT Expr1000,
       Expr1001,
       Expr1003,
       Expr1004
FROM   (VALUES (12,
               $350000,
               350000)) V1(Expr1000, Expr1001, Expr1002)
       OUTER APPLY (SELECT Expr1003,
                           IIF(Expr1001 <> Expr1003, 
                               'Not equal', 
                               'Something else') AS Expr1004
                    FROM   (SELECT CAST(Expr1002 AS MONEY) AS Expr1003) D
                    WHERE  Expr1001 <> Expr1003) OA 

Die vollständige Ausgabe dieses Trace-Flags lautet wie folgt (Und 8605 zeigt im Grunde denselben Baum.)

*** Input Tree: ***
        LogOp_Project COL: Expr1000  COL: Expr1001  COL: Expr1003  COL: Expr1004 

            LogOp_Apply (x_jtLeftOuter)

                LogOp_Project

                    LogOp_ConstTableGet (1) [empty]

                    AncOp_PrjList 

                        AncOp_PrjEl COL: Expr1000 

                            ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=12)

                        AncOp_PrjEl COL: Expr1001 

                            ScaOp_Const TI(money,ML=8) XVAR(money,Not Owned,Value=(10000units)=(-794967296))

                        AncOp_PrjEl COL: Expr1002 

                            ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=350000)

                LogOp_Project

                    LogOp_Select

                        LogOp_Project

                            LogOp_ConstTableGet (1) [empty]

                            AncOp_PrjList 

                                AncOp_PrjEl COL: Expr1003 

                                    ScaOp_Convert money,Null,ML=8

                                        ScaOp_Identifier COL: Expr1002 

                        ScaOp_Comp x_cmpNe

                            ScaOp_Identifier COL: Expr1001 

                            ScaOp_Identifier COL: Expr1003 

                    AncOp_PrjList 

                        AncOp_PrjEl COL: Expr1004 

                            ScaOp_IIF varchar collate 53256,Var,Trim,ML=14

                                ScaOp_Comp x_cmpNe

                                    ScaOp_Identifier COL: Expr1001 

                                    ScaOp_Identifier COL: Expr1003 

                                ScaOp_Const TI(varchar collate 53256,Var,Trim,ML=9) XVAR(varchar,Owned,Value=Len,Data = (9,Not equal))

                                ScaOp_Const TI(varchar collate 53256,Var,Trim,ML=14) XVAR(varchar,Owned,Value=Len,Data = (14,Something else))

            AncOp_PrjList 

*******************