MySQL decimal参数如何精确控制数值精度与范围?

99ANYc3cd6
预计阅读时长 13 分钟
位置: 首页 参数 正文

DECIMAL (或简写为 DEC) 是 MySQL 中用于存储精确数值的数据类型,非常适合存储财务、货币、科学计算等对精度要求极高的数据。

mysql decimal 参数
(图片来源网络,侵删)

核心参数:DECIMAL(M, D)

DECIMAL 类型的定义主要依赖于两个核心参数,它们必须在创建表时指定。

M (总位数 / Precision)

  • 含义:表示数值的总位数,包括整数部分、小数点和小数部分。
  • 范围1 ≤ M ≤ 65,在 MySQL 8.0.17 之前,最大值为 65;从 8.0.17 开始,最大值提升到了 65。
  • 作用:定义了数值的“宽度”或“精度”,确保数字不会超过指定的总长度。

D (小数位数 / Scale)

  • 含义:表示小数点后可以有多少位数字。
  • 范围0 ≤ D ≤ M D ≤ 30,小数位数 D 不能超过总位数 M,并且有一个上限 30。
  • 作用:定义了数值的“小数精度”,确保小数部分的位数是固定的。

语法和示例

基本语法

DECIMAL(M, D)

示例解析

定义 含义 合法示例 非法示例
DECIMAL(5, 2) 总位数 5,小数点后 2 位
整数部分最多有 5 - 2 = 3 位。
45, 99, 00, 99 56 (总位数6)
345 (小数位数3)
DECIMAL(10, 0) 总位数 10,无小数部分
等同于 INT,但存储方式不同,适合大整数。
1234567890, 0 12345678901 (总位数11)
45 (有小数)
DECIMAL(5, 5) 总位数 5,小数点后 5 位
整数部分必须有 0 位,即必须是纯小数。
12345, 99999 2345 (整数部分有1位,总位数6)
DECIMAL(10, 6) 总位数 10,小数点后 6 位
整数部分最多有 10 - 6 = 4 位。
123456, 000001 123456 (整数部分5位,总位数11)

存储空间和计算规则

DECIMAL 的存储方式非常特殊,它不是使用二进制浮点数(如 FLOATDOUBLE),而是使用二进制字符串来存储,这保证了其精确性。

存储空间计算

存储的字节数取决于 MD 的值。

  1. M <= D (即纯小数或小数位数等于总位数):

    mysql decimal 参数
    (图片来源网络,侵删)
    • 需要的字节数 = ceil(M / 9) * 4
  2. M > D (即整数部分存在):

    • 整数部分需要的字节数 = ceil((M - D) / 9) * 4
    • 小数部分需要的字节数 = ceil(D / 9) * 2
    • 总字节数 = 整数部分字节数 + 小数部分字节数

示例:

  • DECIMAL(19, 4):

    • M - D = 15, ceil(15 / 9) = 2 -> 整数部分需要 2 * 4 = 8 字节。
    • D = 4, ceil(4 / 9) = 1 -> 小数部分需要 1 * 2 = 2 字节。
    • 总计需要 10 字节
  • DECIMAL(18, 9):

    mysql decimal 参数
    (图片来源网络,侵删)
    • M - D = 9, ceil(9 / 9) = 1 -> 整数部分需要 1 * 4 = 4 字节。
    • D = 9, ceil(9 / 9) = 1 -> 小数部分需要 1 * 2 = 2 字节。
    • 总计需要 6 字节

一个重要的简化规则: 对于最常见的 DECIMAL(19, D)DECIMAL(M, 4) 等情况,可以记住一个经验法则:

  • M <= 18DECIMAL 通常占用 9 字节
  • M 在 19 到 28 之间,DECIMAL 通常占用 13 字节
  • M 在 29 到 38 之间,DECIMAL 通常占用 17 字节
  • 以此类推,每增加 10 位,增加 4 字节。

DECIMAL vs. FLOAT vs. DOUBLE

这是选择数值类型时最常见的问题。

特性 DECIMAL(M, D) FLOAT DOUBLE
数据类型 精确数值类型 近似数值类型 近似数值类型
存储方式 二进制字符串 IEEE 754 单精度浮点数 IEEE 754 双精度浮点数
精度 精确,不会丢失精度 不精确,存在舍入误差 FLOAT 更精确,但仍有误差
存储空间 可变,通常较大 4 字节 8 字节
适用场景 财务、货币、科学计算(如价格、金额、汇率) 对精度要求不高的科学计算、游戏等 FLOAT 要求更高精度的科学计算
示例 10 存储的就是 10 10 可能存储为 10000000000001 10 可能存储为 09999999999999

为什么不推荐用 FLOATDOUBLE 存储钱? 因为浮点数在二进制中无法精确表示某些十进制小数(如 0.1),在多次计算后,误差会累积,导致最终结果与预期不符,这在财务上是绝对不能接受的。


使用注意事项

  1. 必须指定 MD:在 CREATE TABLE 语句中定义 DECIMAL 列时,必须明确指定 (M, D),否则 MySQL 会报错。

    -- 错误语法
    CREATE TABLE products (
        price DECIMAL -- 必须指定 (M, D)
    );
  2. D 的默认值:如果只指定 M 而不指定 D,MySQL 会将 D 默认设置为 0。

    DECIMAL(10)  -- 等同于 DECIMAL(10, 0)
  3. 计算和转换

    • DECIMAL 参与算术运算时,结果的精度和标度会根据 MySQL 的规则进行计算,结果的标度是操作数中标度的最大值。
    • DECIMAL 与其他类型(如 INTFLOAT)进行运算时,MySQL 会尝试将其他类型转换为 DECIMALDOUBLE,这可能会引入精度损失或类型转换的开销。
  4. 性能DECIMAL 的存储和计算通常比 INTFLOAT 慢一些,因为它不是 CPU 原生支持的数值类型,对于大量数据的计算,性能差异会比较明显,但对于绝大多数应用场景,这种性能影响可以忽略不计。


最佳实践

  1. 存储金额:永远使用 DECIMALDECIMAL(19, 4) 可以支持万亿级别的金额,并精确到万分位,足以应对绝大多数业务场景。

    CREATE TABLE orders (
        id INT PRIMARY KEY,
        amount DECIMAL(19, 4) NOT NULL COMMENT '订单金额'
    );
  2. 存储百分比:根据需求选择,如果需要精确到小数点后多位,使用 DECIMAL,如果只需要近似值,可以使用 DECIMAL(5, 2)FLOAT

    -- 精确的百分比
    discount_rate DECIMAL(5, 4) COMMENT '折扣率,如 0.1234 表示 12.34%'
  3. 权衡精度与空间:不要过度分配 MD,根据业务实际可能的最大值来设定,避免浪费存储空间,如果价格最大是 9999.99,DECIMAL(7, 2) 就足够了,无需使用 DECIMAL(19, 4)

参数 描述 范围 示例
M (Precision) 数值的总位数 1 - 65 DECIMAL(10, 2) -> 67 (总位数7)
D (Scale) 小数点后的位数 0 - 30, 且 D <= M DECIMAL(10, 2) -> 67 (小数位数2)

DECIMAL 的核心是精确,当你的数据不能有任何舍入误差时,它就是你的不二之选。

-- 展开阅读全文 --
头像
2025 ThinkPad X1拆机有何升级与改进?
« 上一篇 01-05
custom版本参数如何正确配置?
下一篇 » 01-05

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]