Ответ 1
Это, безусловно, ошибка в продукте.
Подобная ошибка уже была отмечена и закрыта как "Не исправлена " .
Включая этот вопрос, связанный элемент подключения и еще один два вопроса на этом сайте, я видел четыре случаи такого типа поведения с встроенными TVF и OUTER APPLY
- все они имели формат
OUTER APPLY dbo.SomeFunction(...) F
И вернули правильные результаты при написании как
OUTER APPLY (SELECT * FROM dbo.SomeFunction(...)) F
Итак, это похоже на возможное обходное решение.
Для запроса
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;
План выполнения выглядит как
И список выходных столбцов в финальной проекции. Expr1000, Expr1001, Expr1003, Expr1004.
Однако только два из этих столбцов определены в таблице констант в правом нижнем углу.
Литерал $350000
определяется в таблице констант в правом верхнем углу (Expr1001). Затем он становится внешним, соединенным с таблицей констант в правом нижнем углу. Поскольку никакие строки не соответствуют условию объединения, два указанных там столбца (Expr1003, Expr1004) корректно оцениваются как NULL. то, наконец, вычислительный скаляр добавляет литерал 12
в поток данных как новый столбец (Expr1000) независимо от результата внешнего соединения.
Это не совсем правильная семантика. Сравните с (правильным) планом, когда встроенный TVF настроен вручную.
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
Здесь столбцы, используемые в финальной проекции, Expr1003, Expr1004, Expr1005, Expr1006
. Все они определены в нижнем правом постоянном сканировании.
В случае TVF все, кажется, идет не так очень рано.
Добавление OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606);
показывает, что дерево ввода для процесса уже неверно. Выраженный в SQL это что-то вроде.
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
Полный вывод этого флага трассировки следующий (И 8605 показывает в основном одно и то же дерево.)
*** 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
*******************