decimal(m,d)中m表示总位数(整数+小数),默认值10;d表示小数位数,默认值0。例如decimal(6,2)总位数6、小数2位、整数最多4位。

DECIMAL(M,D) 的 M 和 D 到底怎么算
DECIMAL(6,2) 不是“最多 6 位小数”,而是总位数 6、小数点后占 2 位,整数部分只剩 4 位。插入 12999.00 就会报 ERROR 1264 (22003): Out of range value for column 'price' at row 8——因为 12999 有 5 个整数位,超了 6−2=4 的上限。
常见错误是把 M 当成“字符长度”或“显示宽度”。它实际约束的是数字的**有效数字位数**,且小数点不占位。验证方法很简单:
-
SELECT CAST(12999.00 AS DECIMAL(6,2));→ 直接报错 -
SELECT CAST(12999.00 AS DECIMAL(7,2));→ 返回12999.00
注意:D 不能大于 M,且 D 为负数时(如 DECIMAL(5,-2))表示对百位四舍五入,不是常规用法,上线前务必实测。
INT/TINYINT 类型溢出为什么有时不报错
MySQL 默认行为取决于 sql_mode。如果没启用严格模式,INSERT INTO t1(num) VALUES(2147483648);(超出 INT 上限)会被静默截断为 2147483647,而不会报 ERROR 1264。
这种“自动兜底”在金融、计费等场景极危险。真正该做的不是关严格模式,而是:
- 查当前模式:
SELECT @@sql_mode;,确认是否含STRICT_TRANS_TABLES或STRICT_ALL_TABLES - 生产环境必须开启严格模式,让溢出变成显性错误,而不是数据失真
- 应用层别依赖数据库兜底:Java 用
long接收再校验范围,Python 用decimal.Decimal避免浮点精度干扰
Oracle NUMBER(p,s) 报 ORA-01438 怎么快速定位
Oracle 的 NUMBER(4,2) 表示总共 4 位数字,其中小数点后占 2 位,所以整数部分最多 2 位(4−2)。插入 123.456 时,整数部分 123 已超限,直接报 ORA-01438。
和 MySQL 不同,Oracle 不会静默截断,也不受 session 模式影响,但容易被忽略的是刻度(s)为负的场景:
-
NUMBER(5,-2)表示对百位四舍五入,12345会变成12300,但123456(6 位整数)就超了总位数 5,仍报错 - 用
SELECT DUMP(value) FROM table可看到内部存储字节,辅助判断是否因隐式转换放大了位数 - JPA 实体类若用
@Column(precision=4, scale=2),必须和 DB 定义完全一致,否则 Hibernate 可能绕过校验
字段改大了还报错?检查这三件事
执行完 ALTER TABLE t MODIFY COLUMN price DECIMAL(10,2) 仍报错,大概率不是长度问题,而是底层约束没同步生效:
- 查现存数据最大长度:
SELECT MAX(LENGTH(CAST(price AS CHAR))) FROM t;—— 如果已有数据本身已超新定义,ALTER 不会清理旧值 - 看是否带前缀索引:
SHOW INDEX FROM t WHERE Column_name = 'price';—— 若Sub_part = 2,说明索引只覆盖前 2 位,排序/唯一性判断可能误判 - 确认字符集没干扰:
SHOW CREATE TABLE t;查该列是否被意外定义为latin1,导致数值解析异常(虽少见,但在跨库迁移时发生过)
最常被跳过的一步:ORM 框架缓存了旧表结构。Laravel 的 php artisan schema:dump、Spring Boot 的 spring.jpa.hibernate.ddl-auto=validate 都得重新触发,否则代码里还是按老精度做参数绑定。










