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