Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Outer Apply Restituisce colonne inaspettatamente NOT NULL quando nessuna corrispondenza

Questo è sicuramente un bug nel prodotto.

Un bug simile è già stato segnalato e chiuso come "Non si risolve" .

Includendo questa domanda, l'elemento di connessione collegato e un altro due domande su questo sito ho visto quattro casi di questo tipo di comportamento con TVF in linea e OUTER APPLY - Erano tutti del formato

OUTER APPLY dbo.SomeFunction(...) F

E ha restituito risultati corretti quando scritto come

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

Quindi sembra una possibile soluzione.

Per la domanda

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;

Il piano di esecuzione assomiglia a

E l'elenco delle colonne di output nella proiezione finale è. Espr1000, Espr1001, Espr1003, Espr1004.

Tuttavia solo due di queste colonne sono definite nella tabella delle costanti in basso a destra.

Il letterale $350000 è definito nella tabella delle costanti in alto a destra (Expr1001). Questo viene quindi unito all'esterno nella tabella delle costanti in basso a destra. Poiché nessuna riga corrisponde alla condizione di unione, le due colonne ivi definite (Espr1003, Espr1004) vengono valutate correttamente come NULL. quindi, infine, il calcolo scalare aggiunge il letterale 12 nel flusso di dati come una nuova colonna (Expr1000) indipendentemente dal risultato dell'outer join.

Queste non sono affatto la semantica corretta. Confronta con il piano (corretto) quando il TVF in linea viene inserito manualmente.

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 

Qui le colonne utilizzate nella proiezione finale sono Expr1003, Expr1004, Expr1005, Expr1006 . Tutti questi sono definiti nella scansione costante in basso a destra.

Nel caso della TVF tutto sembra andare storto molto presto.

Aggiunta di OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); mostra che l'albero di input per il processo è già errato. Espresso in SQL è qualcosa di simile.

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 

L'output completo di quel flag di traccia è il seguente (e 8605 mostra sostanzialmente lo stesso albero.)

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

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