Precision (field length) 精度(字段长度)
Scale (decimal places) 范围(小数位数)
例如:-4.75, precision=3,scale=2,和符号位无关
详解
MySQL要求精度大于等于范围:
mysql> create table test (a decimal(1,3));
ERROR 1427 (42000): For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column 'a').
a的定义如下:
create table test (a decimal(3,1));
a字段长度为3,小数点位数为1,表示范围为99.9 ~ -99.9。如果插入的数据小数点位数比scale大,则对scale+1位置的小数进行四舍五入。例如:99.94存储为99.9,99.95提示溢出,99.949存储为99.9(注意,不是这样的四舍五入逻辑哦:99.949=>99.95=>100.0。只看scale后面的一位。)
看一些例子,加深印象:
mysql> create table test (a decimal(3,3));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into test values (11);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (1);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (0.999);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values (0.9999);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (0.0004);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into test values (0.0005);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select * from test;
Field 1: `a`
Catalog: `def`
Database: `test`
Table: `test`
Org_table: `test`
Type: NEWDECIMAL
Collation: binary (63)
Length: 5
Max_length: 5
Decimals: 3
Flags: NUM
+-------+
| a |
+-------+
| 0.999 |
| 0.000 |
| 0.001 |
+-------+
3 rows in set (0.00 sec)
mysql> drop table test;
Query OK, 0 rows affected (0.00 sec)
mysql> create table test (a decimal(3,0));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test values (-999.3);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into test values (-999.7);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (1999);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (1.0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values (1.3);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> insert into test values (-1.3);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> insert into test values (-9.3);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> insert into test values (-0.3);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> select * from test;
Field 1: `a`
Catalog: `def`
Database: `test`
Table: `test`
Org_table: `test`
Type: NEWDECIMAL
Collation: binary (63)
Length: 4
Max_length: 4
Decimals: 0
Flags: NUM
+------+
| a |
+------+
| -999 |
| 1 |
| 1 |
| -1 |
| -9 |
| 0 |
+------+
6 rows in set (0.00 sec)
mysql> drop table test;
Query OK, 0 rows affected (0.01 sec)
mysql> create table test (a decimal(3,1));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test values (1.3);
Query OK, 1 row affected (0.01 sec)
mysql> insert into test values (99.99);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (99.9);
Query OK, 1 row affected (0.01 sec)
mysql> insert into test values (-99.9);
Query OK, 1 row affected (0.01 sec)
mysql> insert into test values (-99.99);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (-99.95);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql> insert into test values (-99.949);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> insert into test values (-99.94);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select * from test;
Field 1: `a`
Catalog: `def`
Database: `test`
Table: `test`
Org_table: `test`
Type: NEWDECIMAL
Collation: binary (63)
Length: 5
Max_length: 5
Decimals: 1
Flags: NUM
+-------+
| a |
+-------+
| 1.3 |
| 99.9 |
| -99.9 |
| -99.9 |
| -99.9 |
+-------+
5 rows in set (0.00 sec)
顺便提一句关于Result Meta的length计算:
对于decimal来说,scale和precision相等的情况下,整数部分的0要占一位length;scale为0的情况下没有小数点,length会少占一位。再考虑到unsigned,如果是无符号数,则length的计算中还不需要考虑符号位,又可以少占一位。为了处理这些逻辑,MySQL对应代码如下:
inline uint32 my_decimal_precision_to_length_no_truncation(uint precision,
uint8 scale,
bool unsigned_flag)
{
/*
When precision is 0 it means that original length was also 0. Thus
unsigned_flag is ignored in this case.
*/
DBUG_ASSERT(precision || !scale);
return (uint32)(precision + (scale > 0 ? 1 : 0) +
(unsigned_flag || !precision ? 0 : 1));
}
看一个数字-4.75来理解上面的逻辑, -4.75, precision=3,scale=2,unsigned_flag=0,则上面的表达式分解如下:
precison算4、7、5三个数字,(scale > 0 ? 1 : 0)算小数点,(unsigned_flag ? 0 : 1)算符号位;根据上面代码中的注释,(!precision ? 0 : 1)表示数值0的情况,此时忽略符号位,也就是说,即使unsigned_flag=1,只要precision为0的话,负号所占的那一位总是不算,-0总是记作0。