Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Outer Apply Retornando colunas inesperadamente NOT NULL quando não há correspondência


Este é certamente um bug no produto.

Um bug semelhante já foi relatado e fechado como "Não vai corrigir" .

Incluindo esta pergunta, o item de conexão vinculado e outro dois perguntas neste site eu vi quatro casos deste tipo de comportamento com TVFs inline e OUTER APPLY - Todos eles eram do formato
OUTER APPLY dbo.SomeFunction(...) F

E retornou resultados corretos quando escrito como
OUTER APPLY (SELECT * FROM dbo.SomeFunction(...)) F

Portanto, isso parece uma possível solução alternativa.

Para a consulta
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;

O plano de execução parece



E a lista de colunas de saída na projeção final é. Expr1000, Expr1001, Expr1003, Expr1004.

No entanto, apenas duas dessas colunas são definidas na tabela de constantes no canto inferior direito.

O literal $350000 é definido na tabela de constantes no canto superior direito (Expr1001). Isso então é unido externamente à tabela de constantes no canto inferior direito. Como nenhuma linha corresponde à condição de junção, as duas colunas definidas lá (Expr1003, Expr1004) são avaliadas corretamente como NULL. então, finalmente, o escalar de computação adiciona o literal 12 no fluxo de dados como uma nova coluna (Expr1000), independentemente do resultado da junção externa.

Estas não são de todo a semântica correta. Compare com o plano (correto) quando o TVF embutido é embutido 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 



Aqui as colunas usadas na projeção final são Expr1003, Expr1004, Expr1005, Expr1006 . Todos estes são definidos na varredura constante inferior direita.

No caso do TVF tudo parece dar errado muito cedo.

Adicionando OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); mostra que a árvore de entrada para o processo já está incorreta. Expresso em SQL é algo como.
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 

A saída completa desse sinalizador de rastreamento é a seguinte (e 8605 mostra basicamente a mesma árvore.)
*** 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 

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