为什么链接服务器在 CASE 表达式中限制为 10 个分支?

And*_*rey 19 sql-server-2005 sql-server-2008 sql-server sql-server-2000 linked-server

为什么这个CASE表达式:

SELECT CASE column 
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        ... c -> i
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END [col] 
FROM LinkedServer.database.dbo.table
Run Code Online (Sandbox Code Playgroud)

产生这个结果?

错误消息:
无法准备消息 8180,级别 16,状态 1,第 1 行语句。
消息 125,级别 15,状态 4,第 1 行
Case 表达式只能嵌套到级别 10。

显然这里没有嵌套CASE表达式,尽管有 10 多个“分支”。

另一个怪事。这个内联表值函数产生相同的错误:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
     @var varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table
)
Run Code Online (Sandbox Code Playgroud)

但类似的多语句 TVF 工作正常:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
    @var varchar(20)
)
RETURNS @result TABLE 
(
    value varchar(max)
)
AS
BEGIN
    INSERT INTO @result
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table

RETURN;
END
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 25

显然这里没有嵌套CASE表达式。

不在查询文本中,不。但是解析器总是将CASE表达式扩展为嵌套形式:

SELECT CASE SUBSTRING(p.Name, 1, 1)
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM AdventureWorks2012.Production.Product AS p
Run Code Online (Sandbox Code Playgroud)

本地查询计划

该查询是本地的(无链接服务器),计算标量定义了以下表达式:

嵌套 CASE 表达式

这在本地执行时很好,因为解析器不会看到CASE超过 10 级深度的嵌套语句(尽管它确实将一个传递到本地查询编译的后期阶段)。

但是,对于链接服务器,生成的文本可能会发送到远程服务器进行编译。如果是这种情况,远程解析器会看到CASE超过 10 级深度的嵌套语句,并且您会收到错误 8180。

另一个怪事。此内联表值函数产生相同的错误

内嵌函数就地扩展到原始查询文本中,因此链接服务器出现相同的错误也就不足为奇了。

但类似的多语句 TVF 工作正常

相似,但不一样。msTVF 涉及到 的隐式转换varchar(max),这恰好防止将CASE表达式发送到远程服务器。因为CASE是在本地计算的,所以解析器永远不会看到过度嵌套CASE并且没有错误。如果您将表定义从结果varchar(max)的隐式类型更改为- 表达式与 msTVF 远程连接,您将收到错误消息。CASEvarchar(2)

最终,当CASE远程服务器评估过度嵌套时会发生错误。如果CASE未在远程查询迭代器中计算 ,则不会产生错误结果。例如,以下包含CONVERT未远程连接的 ,因此即使使用链接服务器也不会发生错误:

SELECT CASE CONVERT(varchar(max), SUBSTRING(p.Name, 1, 1))
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM SQL2K8R2.AdventureWorks.Production.Product AS p
Run Code Online (Sandbox Code Playgroud)

案例未远程


Aar*_*and 6

我的预感是查询正在沿途的某个地方被重写,以具有稍微不同的CASE结构,例如

CASE WHEN column = 'a' THEN '1' ELSE CASE WHEN column = 'b' THEN '2' ELSE ...
Run Code Online (Sandbox Code Playgroud)

我相信这是您使用的任何链接服务器提供程序中的一个错误(实际上可能是所有这些 - 我已经看到它针对几个)。我也相信你不应该屏住呼吸等待修复,无论是在功能上还是在解释行为的令人困惑的错误消息中 - 这已经被报道了很长时间,涉及链接服务器(自从 SQL Server 2000),并且影响的人远少于这个令人困惑的错误消息,在同样的寿命后尚未修复。

正如Paul 指出的那样,SQL Server 正在将您的CASE表达式扩展到嵌套类型,而链接服务器不喜欢它。错误消息令人困惑,但这仅仅是因为表达式的底层转换不是立即可见的(也不直观)。

一种解决方法(除了您添加到问题中的函数更改之外)是在链接服务器上创建一个视图或存储过程,并引用它,而不是通过链接服务器提供程序传递完整查询。

另一个(假设您的查询确实如此简单,并且您只想要字母 az 的数字系数)是:

SELECT [col] = RTRIM(ASCII([column])-96)
FROM LinkedServer.database.dbo.table;
Run Code Online (Sandbox Code Playgroud)

如果您绝对需要它按原样工作,我建议您直接联系支持人员并打开一个案例,尽管我不能保证结果 - 他们可能只是为您提供您在此页面上已经可以访问的解决方法。


小智 6

你可以通过

SELECT COALESCE(
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'a' THEN '1' 
    WHEN 'b' THEN '2' 
    WHEN 'c' THEN '3' 
    WHEN 'd' THEN '4' 
    WHEN 'e' THEN '5' 
    WHEN 'f' THEN '6' 
    WHEN 'g' THEN '7' 
    WHEN 'h' THEN '8' 
    WHEN 'i' THEN '9' 
    ELSE NULL
END,
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'j' THEN '10' 
    WHEN 'k' THEN '11'  
END)
FROM SQL2K8R2.AdventureWorks.Production.Product AS p
Run Code Online (Sandbox Code Playgroud)