【问题标题】:Transact SQL: case when 1=1 then 0.5 else ceiling(sh) end /* returns 1 (!) why? */Transact SQL: case when 1=1 then 0.5 else ceiling(sh) end /* 返回 1 (!) 为什么? */
【发布时间】:2018-11-30 15:26:58
【问题描述】:
-- Transact SQL:  case when 1=1 then 0.5 else ceiling(sh) end   /* returns 1  (!) why? */
declare @T table (h decimal(2,1)) 
insert @T (h) values (1.0)
select 
case when 1=1 then 0.5 else ceiling(sh) end   /* returns 1  (!) why? */
from @T T1
join (select sum(h) as sh from @T )T2  on 1 = 1

【问题讨论】:

  • 它正在转换为 int
  • 因为 ceiling(sh) 返回 int —— 在 case 两边不同的情况下,它将“升级”为 int。在他的回答中查看@JuanCarlosOropeza 代码
  • 其实返回十进制(38,0)
  • @Hogan 它不返回 int 并且 int 没有比十进制更高的数据类型优先级
  • 哦,那是CEILING,当然是numeric(38,0)

标签: sql-server join sum case ceil


【解决方案1】:

答案与int 数据类型无关

  • 文字0.5 的数据类型为decimal(1,1)
  • decimal(p,s) 上应用CEILING 返回类型为 decimal(p,0)
  • decimal(p,s) 上应用SUM 会返回以下结果 输入decimal(38,s)
  • 带有可以返回的混合CASE 表达式 decimal(p1,s1)decimal(p2,s2) 结果将使用与 UNION-ing 这些数据类型并具有精度时相同的规则 (*) max(s1, s2) + max(p1-s1, p2-s2)max(s1, s2) 的规模

* 结果精度和小数位数的绝对最大值为 38。当一个 结果精度大于 38,降为 38, 相应的比例缩小以尽量防止不可分割的部分 被截断的结果。 (source)

所以您的列h 的数据类型为decimal(2,1),应用SUM 时的数据类型为decimal(38,1),应用CEILING 的数据类型为decimal(38,0)。然后你在CASE 表达式中使用它和decimal(1,1)

max(s1, s2) + max(p1-s1, p2-s2)
max( 0,  1) + max(   38,     0) = 1 + 38 = 39

还有

max(s1, s2) = max(0, 1) = 1

因此所需的结果数据类型将是decimal(39,1)。这比 38 大,所以你得到了上面描述的比例缩减,最终得到 decimal(38,0) - 0.5 在转换为该数据类型时四舍五入为 1

如果您希望保持最终结果的精度,您可以使用

case when 1=1 then 0.5 else CAST(ceiling(sh) AS decimal(38,1)) end 

有一个微小的额外溢出风险,但要受到它的影响,总和需要加到以下值之一

  • 9999999999999999999999999999999999999.5
  • 9999999999999999999999999999999999999.6
  • 9999999999999999999999999999999999999.7
  • 9999999999999999999999999999999999999.8
  • 9999999999999999999999999999999999999.9

这样SUM 本身适合38,1,但CEILING 不适合。

【讨论】:

    【解决方案2】:

    更多解释:

    select CEILING(170.00/6.00),CEILING(170/6),170.00/6.00,170/6
    

    结果:

    29  28  28.333333   28
    

    【讨论】:

      【解决方案3】:

      正如documentation 中解释的那样

      当一个操作符组合两个不同数据类型的表达式时,数据类型优先级规则规定将低优先级的数据类型转换为高优先级的数据类型。如果转换不是受支持的隐式转换,则返回错误。当两个操作数表达式具有相同的数据类型时,运算结果具有该数据类型。

      在您的情况下转换为int,因为具有更大的优先级。

      sql demo

      declare @T table (h decimal(2,1)) 
      insert @T (h) values (1.0)
      select 
      case when 1=1 then 0.5 else ceiling(sh)*1.0 end  -- << convert to float
      from @T T1
      join (select sum(h) as sh from @T )T2  on 1 = 1
      

      【讨论】:

      • 两张反对票?也许是因为你没有解释它为什么起作用?
      • 不,它被转换为decimal(38,0),因为this is what ceiling returns。您应该只将 ceiling 部分转换回带有小数位的 decimal
      • 我知道是因为 Martin Smith said so 并且因为 exec sp_describe_first_result_set N'select ceiling(sum(h)) from (values (cast(0.5 as decimal(2,1)))) T1(h)'; 确认了这一点。 Ceiling 保留基本类型(数字),但会更改精度。首先sum() 将其向上推至(38,1),然后ceiling 将其向下四舍五入为(38,0)
      • 将吊顶(sh) 替换为吊顶(h) 后,结果还可以,0.5。问题是,为什么天花板(sh)的工作方式与天花板(h)不同
      • @PetrKral 因为ceiling(h) 给出decimal(2,0),并且在(2,0)(2,1) 之间,结果类型为(3,1)。相反,sum() 将大小推到最大(38),不可能有(39,1),所以最终是(38,0)
      猜你喜欢
      • 2011-01-01
      • 1970-01-01
      • 2020-12-04
      • 2022-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-28
      • 1970-01-01
      相关资源
      最近更新 更多