当我发现一些奇怪的行为时,我在SQL Server中执行了一些简单的财务计算。我试图将一串数字转换为十进制类型。虽然字符串不包含小数点,但我从我的规范中知道字符串中的最后3个位置应该在小数点后面。
我的第一种方法存在缺陷,但是这样做了:
select convert(decimal(11,3),89456123/1000) as TotalUnits
这导致 89456.000 。在演员表演之前执行除法导致小数部分被截断。
所以我把分裂操作移到了演员之外,就像这样:
select convert(decimal(11,3),89456123)/1000 as TotalUnits
这导致小数点后的位置爆炸。它返回 89456.12300000
根据我的十进制规格,我想要11位数,其中3位在小数点后面。现在我有13位总数,其中8位小数点后面。发生了什么事?
为了得到我想要的东西,我想我必须加倍演员,就像这样:
select convert(decimal(11,3), convert(decimal(11,3),89456123)/1000)
,提供 89456.123 。
事实证明,无论我除以什么,产生的小数点爆炸都是一样的。是否将数据类型转换为double或者什么?
我的问题是: 为什么会发生这种情况,并且有一种更优雅的方式来补偿它,而不是双重转换为十进制。
修改 我在SO上找到了similar question,但看起来它们又是双重投射。
答案 0 :(得分:2)
SQL server执行整数运算,强制它使用数字,你可以将它乘以1.0
无需使用转换两次。这使89456.123
无法进行双重转换。
select convert(decimal(11,3),89456123*1.0/1000) as TotalUnits
答案 1 :(得分:2)
为什么convert(decimal(11,3),89456123)/1000
以小数点后6位结尾?规则要求它。 numeric division has rather complicated rules about the resulting type
当您说1.0
时,您最终会得到一个数字,其中最小比例因子可能代表此值:
SELECT SQL_VARIANT_PROPERTY(1.11, 'BaseType')
SELECT SQL_VARIANT_PROPERTY(1.11, 'Precision')
SELECT SQL_VARIANT_PROPERTY(1.11, 'Scale')
SELECT SQL_VARIANT_PROPERTY(1.11, 'TotalBytes')
你应该怎么做?我认为由于复杂的规则,没有真正优雅的解决方案。我能想到的任何解决方案都涉及中间结果的相当疯狂的类型推断。我推荐几乎与RADAR相同的解决方案:
select convert(decimal(11,3), convert(decimal(11, 3), 89456123)/1000) as TotalUnits
主要区别在于我认为*1.0
"技巧"用作演员的简写是混淆代码的含义。如果您碰巧喜欢它,请随意使用它。
答案 2 :(得分:0)
select convert(decimal(11,3),89456123/CONVERT(decimal(11,3),1000))