一个十分怪异的问题,关于decimal类型赋值,高手来看看。

weishaolin131083 2010-09-20 11:34:12

select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b
update #a set a=isnull(a,0)+isnull((select sum(b) from #b),0)
select * from #a --查询的结果应该是 多少
drop table #a
drop table #b

本来我以为结果应该是 0.032 ,可实际运行的结果是 0.030 ,超级怪异。

如果用:

select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b
update #a set a=isnull(a,0)+isnull((select b from #b),0) --改成不用 sum
select * from #a --查询的结果应该是 多少
drop table #a
drop table #b

结果又变成 0.032 了,
...全文
214 点赞 收藏 14
写回复
14 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
SQLCenter 2010-09-20
[Quote=引用 10 楼 weishaolin131083 的回复:]
…只不过是运算符“+”,按道理,+两边,不管用不用 sum,
都只对最后的结果 进行处理,…
[/Quote]

一个极端的情形: [numeric(1,0)=9] + [numeric(1,0)=9]

如果不扩大精度,运算结果肯定溢出,所以结果精度为numeric(2,0)。SQL不可能根据结果来计算精度,那开销就大了。

同理,sum(numeric(1,0)),也不可能根据结果再计算精度,更不可能去计算sum的个数,只是简单做累加操作处理,每次累加精度+1,直到38为止,即使只是sum一个数,也是38。
回复
weishaolin131083 2010-09-20
多谢 SQLCenter 的回答,
但为什么会这样,我还是没弄明白,
只不过是运算符“+”,按道理,+两边,不管用不用 sum,
都只对最后的结果 进行处理,

SQLSERVER为什么要 这样做,很容易造成误用,
所以还是只能靠自己,保证 运算符两边的类型完全一致。
回复
SQLCenter 2010-09-20
decimal/numeric的运算精度问题,MS文档说得比较笼统,过些天我会专门开一个贴子谈这个问题,现在没有太多时间,有兴趣到时关注一下。
回复
SQLCenter 2010-09-20
select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b

-- > sum相当于累加,最终的精度是(38,2)
select sum(b) c into #c from #b
select name, xprec, xscale from tempdb..syscolumns where id = object_id('tempdb.dbo.#c')
/*
name xprec xscale
-------- ----- ------
c 38 2*/

/*
a(18,3) + c(38,2)
为了保证整数部分的精度,所以最终精度取(38,2)
*/

drop table #a,#b,#c
回复
dawugui 2010-09-20
select cast(0 as decimal(18,3)) b into #b
回复
xmx2009 2010-09-20
不好说
回复
SQLCenter 2010-09-20
#3引用错误

[Quote=引用 1 楼 ws_hgo 的回复:]

SQL code
那你就用

select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b
update #a set a=isnull(a,0)+isnull((select b from #b),0)
[/Quote]

如果 b 是多个值,我一定要求和呢?
回复
SQLCenter 2010-09-20
select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b
update #a set a=isnull(a,0) + isnull((select convert(decimal(18,2), sum(b)) from #b),0)
回复
SQLCenter 2010-09-20
[Quote=引用 2 楼 weishaolin131083 的回复:]

我是问 为什么 用:
update #a set a=isnull(a,0)+isnull((select sum(b) from #b),0)
就不行?
[/Quote]

如果 b 是多个值,我一定要求和呢?
回复
weishaolin131083 2010-09-20
我是问 为什么 用:
update #a set a=isnull(a,0)+isnull((select sum(b) from #b),0)
就不行?
回复
ws_hgo 2010-09-20
那你就用

select cast(0.032 as decimal(18,3)) a into #a
select cast(0 as decimal(18,2)) b into #b
update #a set a=isnull(a,0)+isnull((select b from #b),0)
回复
weishaolin131083 2010-09-20
多谢 13 楼提醒,我重新看了下八楼,才明白,
回复
Koppie 2010-09-20
8楼已经说得很清楚了,保证整数的精度。

比如:
$9999.1 + $90000 = $99999.1

假设最大的精度是5位有效数字,那么结果就是 $99999,没理由为了1毛钱舍去9万变成 $9999.9

明白没
回复
weishaolin131083 2010-09-20
问题是,+ 左边是三位小数, +右边是两位小数,
如果要结果准确,那肯定应该 取小数位最多的,
应该取三位才对,

可为什么sqlserver 它要取 右边的两位呢?
不管中间的过程多么复杂,
最后,都是两个decimal数字进行相加,
为什么 过程反而决定结果?
回复
相关推荐
发帖
疑难问题
创建于2007-09-28

2.1w+

社区成员

MS-SQL Server 疑难问题
申请成为版主
帖子事件
创建了帖子
2010-09-20 11:34
社区公告
暂无公告