将int字段改为decimal类型后出现1264 Out of range value的问题分析和处理之二

时间:2021-04-18 16:39:33

有开发同事在生产环境对某个表将int字段改成decimal(10,4)后,提示 1264 Out of range value报错,且原来值为负数,更改字段类型后,全部变为0,咨询dba这个为什么会报错,对数据有什么影响?
我收到这问题后,第一反应就是由于字段类型转换,长度不够发生了数据溢出。
于是,下面对模拟该问题进行测试:
1.建测试表
CREATE TABLE `zeng1` (
  `_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `a` int(32) DEFAULT NULL,
PRIMARY KEY (`_id`)
) ENGINE=InnoDB AUTO_INCREMENT=28129 DEFAULT CHARSET=utf8 ;

2.准备测试用数据
insert into zeng1(a) values (70000);
insert into zeng1(a) values (-123);
insert into zeng1(a) values (-10);

 select * from zeng1;
+-------+-------+
| _id   | a     |
+-------+-------+
| 28129 | 70000 |
| 28130 |  -123 |
| 28131 |   -10 |
+-------+-------+
3.修改字段类型

alter table zeng1 modify column a decimal(10,4) unsigned;
Query OK, 3 rows affected, 2 warnings (0.03 sec)  
Records: 3  Duplicates: 0  Warnings: 2

4.查看warnings
(product)root@localhost [midea_gls]> show warnings;
+---------+------+--------------------------------------------+
| Level   | Code | Message                                    |
+---------+------+--------------------------------------------+
| Warning | 1264 | Out of range value for column 'a' at row 2 |
| Warning | 1264 | Out of range value for column 'a' at row 3 |
+---------+------+--------------------------------------------+
2 rows in set (0.00 sec)

上面显示第2和第3行发生了数据溢出。

5.查询类型更改后的数据
select * from zeng1;
+-------+------------+
| _id   | a          |
+-------+------------+
| 28129 | 70000.0000 |
| 28130 |     0.0000 |
| 28131 |     0.0000 |
+-------+------------+
3 rows in set (0.00 sec)
第四和第五行的a字段记录发生了溢出。应更改将字段定义为带符号的,修改字段如下:
alter table zeng1 modify column a decimal(10,4);

再把数据update:
update zeng1 set a=-123 where `_id`= 28130;
update zeng1 set a=-10 where `_id`= 28131;
查看变为正常了:
select * from zeng1;
+-------+------------+
| _id   | a          |
+-------+------------+
| 28129 | 70000.0000 |
| 28130 |  -123.0000 |
| 28131 |   -10.0000 |
+-------+------------+
3 rows in set (0.00 sec)

 decimal(10,4)的取值范围为-999999.9999到999999.9999,decimal(10,4)中的10表示全部位数为10(整数位+小数位),4表示小数位。
show warnings看到out of range表示超过了(小于最小值或大于最大值)这个值 ,建议生产环境在对一个表更改字段前,先对这个表的数据create table as select 来备份一份,若出现out of range时,
该记录的数据将全部变成0或9。
1.确保decimal(M,N)中M-N>=max(length(a)),在更改前可以查询字段a值的最大长度:
select max(length(a)) from zeng1;
2.确保有负数值的字段定义为带符号的,decmial默认为带符号,即不加 unsigned。检查字段a是否有负数值,用下面查询检查:
select count(*) from zeng1 where a like '-%',若返回值大于0,表示存在负数。

若前面在更改字段前,先对表进行了备份,表名为zeng1_bak,更改字段后,如何将数据更新回去?
select * from zeng1;
+-------+------------+
| _id   | a          |
+-------+------------+
| 28129 | 70000.0000 |
| 28130 |     0.0000 |
| 28131 |     0.0000 |
+-------+------------+
3 rows in set (0.00 sec)

select * from zeng1_bak;
+-------+-------+
| _id   | a     |
+-------+-------+
| 28129 | 70000 |
| 28130 |  -123 |
| 28131 |   -10 |
+-------+-------+
更新如下:
update zeng1 a1 set a1.a=(
select b1.a from zeng1_bak b1 where b1.`_id`=a1.`_id` and b1.`_id` in (28130,28131));

select * from zeng1;
+-------+------------+
| _id   | a          |
+-------+------------+
| 28129 | 70000.0000 |
| 28130 |  -123.0000 |
| 28131 |   -10.0000 |
+-------+------------+
3 rows in set (0.00 sec)