加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

不小心又中了decimal的埋伏——重新捋一下decimal的相关知识

发布时间:2020-12-12 14:56:14 所属栏目:MsSql教程 来源:网络整理
导读:大家都知道在程序语言和数据库中各种数字类型decimal,numeric,float,double,real……,有时候不小心就会出现问题,除了掌握基本的数据存储原理,还要仔细了解各个语言和产品里的细节差别,即便如此,一不小心还是有可能中了埋伏。 在SQLserver中Decimal可以
大家都知道在程序语言和数据库中各种数字类型decimal,numeric,float,double,real……,有时候不小心就会出现问题,除了掌握基本的数据存储原理,还要仔细了解各个语言和产品里的细节差别,即便如此,一不小心还是有可能中了埋伏。 在SQLserver中Decimal可以用来存储整数,也可以用来存储小数,而且精度高,所以我常常用来保存像销售类的数据相关字段。

情景再现

某数据表结构如下:
create table demo
(
id bigint,amount decimal(18,5),--总金额
quantity decimal(18,5)--数量
)
在编写一个功能其中使用了一句
select convert(decimal(18,2),amount)/convert(decimal(18,quantity) from demo where quantity>0.00

问题解决和分析

执行过程中总是报“被零除的错误”,看看代码觉得没有什么问题,仔细看后才明白,原本想我要的结果只需要保留2位小数,所以执行转换后再行计算,还有一方面是quantity>0.00的判断和前面的转换不匹配造成的。尽管0.00001虽然大于0.01符合筛选条件
但转化后就变成0.00了,所以才会出现上面的错误。 sql 2005中decimal的能够存储的数字范围是- 10^38 +1 到 10^38 - 1(见参考1),用科学计数法看不直观它的威力(^-^),最大正数就是4个1亿相乘以后再乘以1万减1。这是非常大的数字了。 回到正题,那么我们定义的两个字段都是decimal(18,5),表示精度为18,小数为5位, 但是要注意的是ms sql 中位数就是10进制的位数,也就是说decimal(18,5)表示-9999999999999.99999到9999999999999.99999之间的数字。这样decimal(18,5)和decimal(18,2)就有较大差别了,可以认为它们是不同的数据类型(见参考2),在两种数据数据类型之间转换,自然就可能有所损失。 解决就简单了,直接改为计算后在转化就行了。
select convert(decimal(18,2)/(amount/quantity)) from demo where quantity>0.00000

反思

其实解决以上问题很简单。之所以“小题大做”,主要还是觉得对数据类型掌握还不够细,总是觉得那么数据类型,大概对就行了,差不多就行,实际上这是不对的。

延伸思考,看看C#中decimal

在开发中常常C#来读取数据库存储的值,就必要要定义相应的变量来存取。在C#里decimal是128位的精确数据类型,大致范围是?±1.0 × 10^-28 到 ±7.9 × 10^28,比SQL Decimal的最大范围要小一些,但是当前人类的99.999999……999999999%的应用应该是完全可以满足的(见参考3)。decimal在计算机中是如何存储? (见参考4)。 ? 参考1: decimal 和 numeric (Transact-SQL) 参考2: SQL SERVER – Difference and Explanation among DECIMAL,FLOAT and NUMERIC
参考3: decimal (C# Reference) 参考4: http://topic.csdn.net/u/20090505/14/2859ab0f-579a-467b-b03a-c792bfb98bc6.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读