环境:

  • window10
  • vs2022
  • .net 6
  • mysql 8.0.25
  • DBeaver

参考:
《mysql:11.1.3 Fixed-Point Types (Exact Value) - DECIMAL, NUMERIC》
《MSDN:浮点数值类型(C# 引用)》

1. decimal和numeric的定义

这两个是同一个意思,使用numeric定义的列会被转换成decimal。

使用的语法为:
decimal[M[,D]]
如: decimaldecimal(5)以及decimal(5,2)都是可以的。

上面的M表示总共有多少数字,D表示小数点后面有几位。
如:45.123M是5,d是3。

注意:M的默认值是10,最大值是65,D的默认值是0,最大值是30。

看下面测试:

create table test.test(
	t_decimal decimal,
	t_decimal5 decimal(5),
	t_decimal5_2 decimal(5,2),
	t_numeric numeric,	
	t_numeric_4 numeric(4),
	t_numeric_4_3 numeric(4,3)
)
-- 查看定义语句
show create table test.test

-- 查看列类型
select c.TABLE_SCHEMA ,c.TABLE_NAME ,c.COLUMN_NAME ,c.ORDINAL_POSITION,c.DATA_TYPE,c.NUMERIC_PRECISION ,c.COLUMN_TYPE ,c.NUMERIC_SCALE 
from information_schema.`COLUMNS` c  where TABLE_SCHEMA ='test' and TABLE_NAME ='test' order by ORDINAL_POSITION 

输出如下:
在这里插入图片描述
在这里插入图片描述
可以看到:numeric被转换成decimal了,MD的默认值也体现了出来。

2. 数据库插入decimal数据的过程

如果我们定义decimal(5,2),那么当我们插入数据时,会先检查小数点前是否超过3位(5-2),超过的直接报异常,然后在检查小数点后的位数是否超过2位,如果超过的话就四舍五入,最后插入到数据库。

如:

  • 插入123.4512.1时,小数点前面没有超过3位,小数点后面也没有超过2位,直接插入到数据库;
  • 插入123.123时,小数点前面没超过3位,小数点后面超过2位,四设五入为 123.12,最终数据库中插入123.12
  • 插入1234.12时,小数点前面超过3位,直接报错:Out of range value
  • 插入1234.123时,小数点前面超过3位,直接报错:Out of range value

测试代码如下:

create table test.test(
	t_decimal decimal,
	t_decimal5 decimal(5),
	t_decimal5_2 decimal(5,2),
	t_numeric numeric,	
	t_numeric_4 numeric(4),
	t_numeric_4_3 numeric(4,3)
)
insert into test.test(t_decimal,t_decimal5,t_decimal5_2,t_numeric,t_numeric_4,t_numeric_4_3) 
	values(1234567891,12345,123.45,1234567891,1234,1.234);

insert into test.test(t_decimal5_2) values(123.45);-- 正常插入
insert into test.test(t_decimal5_2) values(12.1);-- 正常插入
insert into test.test(t_decimal5_2) values(123.123); -- 数据被四设五入
insert into test.test(t_decimal5_2) values(1234.123); -- 报错: Out of range value
insert into test.test(t_decimal5_2) values(1234.123); -- 报错: Out of range value

select * from test.test

在这里插入图片描述

3. 测试MD的最大值:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. c#中的使用方式

mysql中的decimal是定点小数,存储的是精确值,而c#中的float、double、decimal都是浮点型,存储的是近似数,所以c#中并没有很对称的数据类型。

但一般,我们使用c#中的decimal去表示mysql中的decimal也没问题,因为它的精度已经很高了,如下图:
在这里插入图片描述

为什么c#中没有定点小数呢,毕竟c#要考虑计算得。而数据库主要的任务是存储,事实上一旦在数据库中做运算,那么看似没问题的数据类型也会暴露出问题的。

如果,我们实在担心精度丢失,那么就直接用 string表示吧(注意:数据库和c#都要是string,因为ado.net自动读取后就是decimal已经失真了)。

下面是做了decimal失真的实验:

create table test(
	t_decimal28_15 decimal(28,15),
	t_decimal30_15 decimal(30,15)
)

insert into test(t_decimal28_15,t_decimal30_15) values 
    (1234567890123.123456789012345,123456789012345.123456789012345)
select * from test

输出:
在这里插入图片描述
可以看到,mysql中存储和读取都是没问题的,因为mysql使用定点小数存储,精度范围足够大,不会失真(decimal(M,D),M最大是65,D最大是30)。

使用c#读取:

//<PackageReference Include="MySqlConnector" Version="2.1.6" />
var connstring = "Server=127.0.0.1;Database=test;Uid=root;Pwd=123456;";
var conn = new MySqlConnector.MySqlConnection(connstring);
conn.Open();
var cmd = conn.CreateCommand();

cmd.CommandText = @"drop table  if exists test";
cmd.ExecuteNonQuery();

cmd.CommandText = @"create table test(
	t_decimal28_15 decimal(28,15),
	t_decimal30_15 decimal(30,15)
)";
cmd.ExecuteNonQuery();
cmd.CommandText = @"insert into test(t_decimal28_15,t_decimal30_15) values
	(1234567890123.123456789012345,123456789012345.123456789012345)";
cmd.ExecuteNonQuery();

cmd.CommandText = "select * from test";
var reader = cmd.ExecuteReader();
reader.Read();
var t_decimal28_15 = reader.GetValue(0);

//true,不失真
Console.WriteLine(t_decimal28_15.ToString() == "1234567890123.123456789012345");
var t_decimal30_15 = reader.GetValue(1);

// false,出现失真(读取到的为:123456789012345.12345678901235,即最后一位小数发生四舍五入)
Console.WriteLine(t_decimal30_15.ToString() == "123456789012345.123456789012345");

reader.Close();
conn.Close();

虽然上面c#测试的失真了,但是,我们真的需要这么高的精度吗?一般是不需要的。如果需要,请使用使用string存储编程。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐