CASE THEN 子句总是被评估

Posted

技术标签:

【中文标题】CASE THEN 子句总是被评估【英文标题】:CASE THEN clause always evaluated 【发布时间】:2012-08-10 09:01:09 【问题描述】:

我正在做一个 SELECT,它使用 CASE 将 nvarchar 值转换为适当的类型,如下所示:

SELECT CASE 
    WHEN @propType = 'money' THEN convert(money, datavalue)
    [...]
    ELSE datavalue
END
FROM [...]

然而,convert 似乎总是被执行,即使@propType 等于钱。可运行示例:

declare @proptype nvarchar(50)= 'nvarchar'
declare @val nvarchar(10) = 'test'
select 
    case @proptype
        when 'money' then convert(money, @val)
        else @val
    end

为什么会这样,我该如何解决? MSDN 文档是这样说的:

CASE 语句按顺序评估其条件并停止 与满足条件的第一个条件。在一些 情况下,表达式在 CASE 语句之前计算 接收表达式的结果作为其输入。错误 评估这些表达式是可能的。聚合表达式 出现在 CASE 语句的 WHEN 参数中,首先求值,然后 提供给 CASE 语句。例如,以下查询 在产生 MAX 的值时产生除以零错误 总计的。这发生在评估 CASE 表达式之前。

我不确定这是否相关,但语言对于非本地人来说有些沉重,所以也许是这样?

【问题讨论】:

在将varchar 隐式转换回varchar 时,将其显式转换为money 的唯一目的是,出于格式化目的,为什么不将其存储在所需的格式中开始还是让客户处理格式? 源数据不在我的控制之下。在实际情况下,我必须跳很多圈才能将数据转换成这样一种格式,以便我可以将其插入到它所指定的报告表中(正确键入) 【参考方案1】:

看看下面Use caution when Using CONVERT() with CASE or IF functions in Transact SQL (T-SQL)

最初的想法一般是以下一种“自从第一次 评估的值是数字,它被转换为十进制,所有其他 数据也应该是小数”或“如果 SQL Server 能够 将任何值转换为指定类型,然后所有值都是 预计是转换后的类型”。但是,这是不正确的 (虽然第二个很接近)!

真正的问题是,如果您选择在任何地方转换值 在 Case 语句中,您正在转换值的数据类型 to 是所有值的预期类型,无论它们是否属于 那个类型与否。此外,即使 NONE 的值实际上可以是 已转换(即使 Convert 代码行从未执行),所有 这些值仍应为指定的类型 转换函数!

【讨论】:

谢谢,这说明了原因,但真的没有办法了吗?【参考方案2】:

要明确发生了什么,then 子句被评估。

如果你这样做,你会看到同样的错误

SELECT CASE @proptype
         WHEN 'money' THEN $1.0 /*<-- Literal of datatype money*/
         ELSE @val
       END 

CASE 的文档解释了返回类型

从类型集中返回最高优先级的类型 result_expressions 和可选的else_result_expression。更多 信息,请参阅Data Type Precedence (Transact-SQL)。

money 具有比 nvarchar 更高的数据类型优先级,因此评估 else @val 然后将其转换为 money 并失败。

一种可能的解决方法是将其转换为 sql_variant,因为这比两者具有更高的数据优先级。

declare @proptype nvarchar(50)= 'nvarchar'
declare @val nvarchar(10) = 'test'
select 
    case @proptype
        when 'money' then convert(money, @val)
        else cast(@val as SQL_VARIANT)
    end

【讨论】:

以上是关于CASE THEN 子句总是被评估的主要内容,如果未能解决你的问题,请参考以下文章

LISTAGG 被评估并在无法访问的 case 语句中失败

Spark else() 总是被评估

&& 之后的条件是不是总是被评估

没有短路评估的 CASE 表达式?

JOIN 子句中的 MySQL 逻辑评估是不是延迟/短路?

SQL IN 子句保证评估为假