Rac*_*SQL 3 sql-server-2008 sql-server view linked-server
我有这个查询:
SELECT
-
FROM -.dbo.tb1
WHERE
-IN
(
SELECT
CASE
WHEN EXISTS
(
SELECT *
FROM -.dbo.tb2U1 WITH (NOLOCK)
WHERE col1 = '8614'
)
THEN
(
SELECT TOP (1)
CASE WHEN ISNULL(col2,0) IN (1,2,3,6,8)
THEN '2'
ELSE '1'
END AS col3
FROM -.dbo.tb1U1 WITH (NOLOCK)
LEFT join -.dbo.tb3U2 WITH (NOLOCK)
ON U1.-= U2.-
WHERE
-= '8614'
ORDER by
-DESC
)
ELSE '3'
END
);
Run Code Online (Sandbox Code Playgroud)
当我运行它时-,它没问题。它返回我想要的东西:
SELECT
-
FROM -.dbo.tb1
WHERE
-IN
(
SELECT
CASE
WHEN EXISTS
(
SELECT *
FROM -.dbo.tb2U1 WITH (NOLOCK)
WHERE col1 = '8614'
)
THEN
(
SELECT TOP (1)
CASE WHEN ISNULL(col2,0) IN (1,2,3,6,8)
THEN '2'
ELSE '1'
END AS col3
FROM -.dbo.tb1U1 WITH (NOLOCK)
LEFT join -.dbo.tb3U2 WITH (NOLOCK)
ON U1.-= U2.-
WHERE
-= '8614'
ORDER by
-DESC
)
ELSE '3'
END
);
Run Code Online (Sandbox Code Playgroud)
但是如果我运行它-(我们有相同的数据库-,但没有表,只有视图重定向到-具有相同表名的视图)它会显示这个错误:
col2
-------------------------------------------------- Value
Run Code Online (Sandbox Code Playgroud)
链接服务器的视图采用以下形式:
Msg 8180, Level 16, State 1, Line 1 Statement(s) could not be prepared.
Msg 4145, Level 15, State 1, Line 1 An expression of non-boolean type specified in a
context where a condition is expected, near 'THEN'.
Run Code Online (Sandbox Code Playgroud)
如果我在没有SELECT这里的情况下编写查询:
SELECT *
FROM -.-.dbo.tb2
Run Code Online (Sandbox Code Playgroud)
...它运行完美。
我只想知道为什么会发生这种情况。这怎么可能?这是相同的查询。
这是一个错误,尽管只有在非常特定的情况下才会出现。
SQL Server 使用称为分布式查询 (DQ) 的组件来构建 T-SQL 命令以针对链接服务器运行。优化器将CASE包含子选择的表达式转换为在本地对象上运行良好的形式,但没有正确转换为远程查询形式(在这种特殊情况下)。
该错误导致生成需要本地计算结果的远程节点。创建一个内部查询树,期望这些计算值作为参数,但不生成任何参数。仅当子选择与外部查询不相关时才会发生此错误(如此处的情况),因为应用了仅对本地查询安全的简化。
DQ 生成带参数的远程查询(使用标准?语法),但没有传递参数,从而导致"statement cannot be prepared"错误(准备好的语句是参数化语句的技术名称)。
如果子查询包含外部引用(相关)而不是文字值,则不会应用仅限本地的简化,并且查询树将是正确的。
再生产
创建SQL2008到另一个 SQL Server 2008 实例的链接服务器,其中包含AdventureWorks示例数据库。在本地实例上创建以下视图:
CREATE VIEW dbo.Product AS
SELECT *
FROM SQL2008.AdventureWorks.Production.Product AS P;
GO
CREATE VIEW dbo.TransactionHistory AS
SELECT *
FROM SQL2008.AdventureWorks.Production.TransactionHistory AS TH;
Run Code Online (Sandbox Code Playgroud)
为以下查询生成一个估计的执行计划(当然,运行它只会产生错误消息):
SELECT
P.ProductID
FROM dbo.Product AS P
WHERE P.ProductID IN
(
SELECT -- Comment this
CASE
WHEN EXISTS
(
SELECT *
FROM dbo.TransactionHistory AS TH
WHERE TH.ProductID = 1 -- Not correlated
)
THEN
(
SELECT TOP (1)
TH.TransactionID
FROM dbo.TransactionHistory AS TH
WHERE
-- Not correlated
TH.ProductID = 1
ORDER BY
TH.TransactionID DESC
)
ELSE 0
END
);
Run Code Online (Sandbox Code Playgroud)
下面的估计查询计划突出显示了问题运算符:

该Remote Query运算符的属性是:
SELECT "Tbl1001"."ProductID" "Col1019"
FROM "AdventureWorks"."Production"."Product" "Tbl1001"
WHERE CASE WHEN ? THEN ? ELSE (0) END = "Tbl1001"."ProductID"
Run Code Online (Sandbox Code Playgroud)
注意?参数标记。当远程服务器尝试编译时,未解析的参数会生成第二条错误消息WHEN ? THEN ?。
如果注释掉标记的SELECT,则不会应用有问题的转换,并且不会生成错误的远程查询。
同样,TH.ProductID = 1例如,用关联替换任一实例TH.ProductID = P.ProductID也会生成正确的远程查询。
此错误会在所有当前版本的 SQL Server 中重现,直到并包括 SQL Server 2014 RTM CU4。