Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Outer Apply Renvoyer des colonnes de manière inattendue NOT NULL lorsqu'il n'y a pas de correspondance

Il s'agit certainement d'un bug du produit.

Un bogue similaire a déjà été signalé et fermé comme "Ne répare pas" .

Y compris cette question, l'élément de connexion lié et un autre deux questions sur ce site, j'ai vu quatre cas de ce type de comportement avec des TVF en ligne et OUTER APPLY - Tous étaient au format

OUTER APPLY dbo.SomeFunction(...) F

Et a renvoyé des résultats corrects lorsqu'il est écrit comme

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

Cela ressemble donc à une solution de contournement possible.

Pour la requête

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;

Le plan d'exécution ressemble à

Et la liste des colonnes de sortie dans la projection finale est. Expr1000, Expr1001, Expr1003, Expr1004.

Cependant, seules deux de ces colonnes sont définies dans le tableau des constantes en bas à droite.

Le littéral $350000 est défini dans le tableau des constantes en haut à droite (Expr1001). Celui-ci est ensuite joint à l'extérieur sur le tableau des constantes en bas à droite. Comme aucune ligne ne correspond à la condition de jointure, les deux colonnes qui y sont définies (Expr1003, Expr1004) sont correctement évaluées comme NULL. puis enfin le scalaire de calcul ajoute le littéral 12 dans le flux de données en tant que nouvelle colonne (Expr1000) quel que soit le résultat de la jointure externe.

Ce n'est pas du tout la bonne sémantique. Comparez avec le plan (correct) lorsque le TVF en ligne est intégré manuellement.

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 

Ici les colonnes utilisées dans la projection finale sont Expr1003, Expr1004, Expr1005, Expr1006 . Tous ces éléments sont définis dans le balayage constant en bas à droite.

Dans le cas de la TVF, tout semble mal tourner très tôt.

Ajout de OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); montre que l'arborescence d'entrée du processus est déjà incorrecte. Exprimé en SQL, c'est quelque chose comme.

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 

La sortie complète de cet indicateur de trace est la suivante (et 8605 affiche essentiellement le même arbre.)

*** 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 

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