在链接服务器上使用视图时出错

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)

...它运行完美。

我只想知道为什么会发生这种情况。这怎么可能?这是相同的查询。

Pau*_*ite 6

这是一个错误,尽管只有在非常特定的情况下才会出现。

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。