Mysql之DECIMAL

3,642 阅读4分钟

前言

本文为阅读MySql官方文档的翻译,为笔者的阅读和扩展路径。

简介

官方文档

DECIMAL和NUMERIC类型(定点类型)的存储精度数值的数据值。当保持精确精度是很重要时使用这些类型,例如货币数据。在MYSQL中,NUMERIC被实现为DECIMAL,所以以下关于DECIMAL的评论同样适用于NUMERIC。

MYSQL以二进制格式存储值。请参见12.25 Precision Math

在DECIMAL列声明中,可以指定(并且通常)指定精度和小数位数。例如:

salary DECIMAL(5,2)

在本例中,5是精度,2是小数位。精度表示存储值的有效位数,小数表示可以存储在小数点后的位数。

标准SQL要求DECIMAL(5,2)能够存储任何五位数字和两位小数的值,因此可以存储在salary列中的值范围从-999.99到999.99。

在标准的SQL中,语法DECIMAL(M)等同于DECIMAL(M,0)。类似的,语法DECIMAL等同于DECIMAL(M,0),MySQL支持这两种语法,M的默认值位10。

如果scale是0,则DECIMAL值不包含小数点和小数部分。

DECIMAL最大位数为65,但给定DECIMAL列实际范围受指定的精度和小数位限制。因此当该列是被分配超过指定范围时,该值将转换成指定范围内。(确切的行为是特定于操作系统的,但通常效果时截断到允许的位数。)

DECIMAL数据类型特征

官方文档

本节讨论DECIMAL数据类型(及其同义词)的特征,特别注意以下主题:

  • 最大位数
  • 存储格式
  • 存储要求
  • 非标准MySQL扩展到DECIMAL列的上限。

DECIMAL列的声明语法是DECIMAL(M,D)。参数取值的范围如下:

  • M是最大数(精度)。它的范围是1到65.
  • D是小数点右侧的位数(刻度)。它的范围是0到30,并且不能大于M。

如果D省略,则默认为0。如果M省略,则默认为10。

M的最大值65意味着DECIMAL值的计算是精确到65位。65位精度的限制也适用于精确数值字面量,因此此类字面量的最大范围与之前不同。(DECIMAL的文本的长度也有限制;请参阅 第 12.25.3 节,“表达式处理”。)

DECIMAL列值是存储使用二进制格式,将9个十进制数字打包成4个字节。每个小数的整数部分和小数部分的存储要求是分别明确的。每个九位数的倍数需要4个字节,剩下的任何剩余数字都需要4个字节的一部分。下表给出了剩余数字所需的存储空间。

剩余数字字节数
00
1-21
3–42
5–63
7–94

例如,DECIMAL(18,9)列的小数点两边各有九位,因此整数部分和小数部分各需要4个字节。 DECIMAL(20,6)列有14个整数位和6个小数位。整数位中的九位需要四个字节,其余五位需要三个字节,六位小数需要三个字节。

DECIMAL列不存储前导+字符或者前导-字符或者前导0数字。如果插入+0003.1到DECIMAL(5,1)列中,它将存储3.1。对于负数,-字符是不被存储。

DECIMAL列不允许值大于列定义所暗示的范围。例如,DECIMAL(3,0)列支持范围为-999至999。 DECIMAL(M,D)小数点左边允许最多有M-D位。

SQL标准要求NUMERIC(M,D)的精度是确切的M位。对于DECIMAL(M,D),标准要求精度至少为M位,但允许更多。在MySQL中,DECIMAL(M,D) 和 NUMERIC(M,D) 是相同的,并且都具有 M 位的精度。

有关DECIMAL值的内部格式的完整说明 ,请参阅MySQL发布的源代码strings/decimal.c文件。该格式在decimal2bin()函数中进行了解释(通过示例) 。

decimal_t

下面为decimal的数据结构,不代表实际存储。

typedef int32 decimal_digit_t; 

struct decimal_t { 
    int intg, frac, len; 
    bool sign; 
    decimal_digit_t *buf; 
};
  • intg表示整数部分的数字个数
  • frac表示小数部分的数字个数
  • sign表示符号(正负)
  • len表示数组长度,在MySQL实现中恒为9,表示存储的上限,而 buf 实际有效的部分, 则是由 intg 和 frac 共同决定
  • *buf表示存储decimal的数字

例如

// 123.45 decimal(5, 2) 整数部分为 3,  小数部分为 2
decimal_t dec_123_45 = {
  int intg = 3;
  int frac = 2;
  int len = 9;
  bool sign = false;
  decimal_digit_t *buf = {123, 450000000, ...};
};

总结

  • DECIMAL位定点类型,可以精确的表示规定精度
  • DECIMAL(M,D) M表示精度,D表示小数位。
  • M默认为10。标准MySQL,M最大为65,超出部分通常截断。
  • D默认为0,最大为30且不能大于M。
  • DECIMAL存储使用二进制格式,整数和小数是分开存储的,用4个字节表示9个十进制数。