跳到主要内容

数值类型

数值类型用于保存可以参与数值运算的数据,例如数量、价格、比例、温度和统计结果。选对类型,既能避免溢出和精度丢失,也能让表结构准确表达业务规则。

本文以通用 SQL 为主。各数据库对类型名称、存储范围、隐式转换和自增列的实现并不完全相同;示例投入生产前,应核对目标数据库及版本的文档。

三类数值类型​

类别常见类型典型特点常见用途
整数SMALLINT、INTEGER、BIGINT没有小数部分,范围和存储空间通常随位数增加数量、状态码、主键、计数
精确数值DECIMAL(p, s)、NUMERIC(p, s)按十进制精确表示,适合需要可预测结果的数据金额、税率、单价、财务统计
近似数值REAL、FLOAT、DOUBLE PRECISION用有限位二进制近似表示,范围大、运算通常较快,但可能有误差科学计算、传感器测量、统计模型

不要只看列中“都是数字”就统一使用某一种类型:整数和小数的业务含义不同,精确与近似也有不同的误差模型。

整数类型​

常见整数类型​

SMALLINT、INTEGER(也常写作 INT)和 BIGINT 都表示整数。通常,类型越大,可表示的范围越大,占用的存储空间也可能越多:

类型常见 32 位有符号实现的范围选择提示
SMALLINT-32,768 到 32,767范围很小且明确时使用
INTEGER-2,147,483,648 到 2,147,483,647一般计数和普通整数的默认选择
BIGINT约 -9.22×10¹⁸ 到 9.22×10¹⁸大规模计数、时间戳或可能增长很大的标识

上表是常见实现,不是所有数据库的保证;具体范围以目标数据库为准。无符号整数、TINYINT 等类型属于数据库方言,迁移 SQL 时不要默认它们可用。

整数溢出​

当结果超过类型可表示的范围时,数据库可能报错、截断、转换成更宽类型或得到方言相关结果。不要等数据接近上限才处理:

CREATE TABLE inventory (
product_id BIGINT NOT NULL,
quantity INTEGER NOT NULL,
CONSTRAINT ck_inventory_quantity CHECK (quantity >= 0)
);

CHECK 可以防止负库存进入表中,但它不能防止应用计算过程中的中间结果溢出。涉及大数相乘、累加时,应提前评估表达式类型,并在应用层使用足够宽的类型。

整数不等于“只有数字的文本”​

电话号码、邮政编码、证件号和商品编码即使只含数字,也通常不应使用整数:它们可能有前导零,也不参与加减乘除。

postal_code VARCHAR(10) NOT NULL

精确数值:DECIMAL 与 NUMERIC​

精度与小数位​

DECIMAL(p, s)(NUMERIC(p, s) 通常是同义写法)中的:

  • p(precision,精度)表示总有效位数;
  • s(scale,小数位数)表示小数点右侧最多保存多少位;
  • 整数部分最多约有 p - s 位。

例如,DECIMAL(12, 2) 最多保存 12 位有效数字,其中 2 位在小数点后,因此整数部分通常最多 10 位:

price DECIMAL(12, 2) NOT NULL,
tax_rate NUMERIC(7, 4) NOT NULL

DECIMAL(12, 2) 能表达 1234567890.12,但不能表达更大的整数部分。p 和 s 的上限、超出范围时的行为以及存储空间由数据库决定。

舍入和超出小数位​

插入的小数位多于声明的 s 时,数据库可能按规则舍入,也可能拒绝写入;不要把数据库自动舍入当作业务规则。需要明确金额精度时,应在应用层或 SQL 中显式处理,并对边界值编写测试:

SELECT CAST(12.345 AS DECIMAL(10, 2));

不同数据库对 CAST 的舍入方向、负数边界和错误模式可能有差异。涉及结算、税费或对账时,应以目标数据库的实际测试结果为准。

金额为什么不用浮点数​

二进制浮点数通常无法精确表示许多十进制小数,例如 0.1。连续相加后可能出现人眼不期望的尾差:

-- 账务金额通常应使用 DECIMAL/NUMERIC
CREATE TABLE order_items (
unit_price DECIMAL(12, 2) NOT NULL,
quantity INTEGER NOT NULL,
line_amount DECIMAL(14, 2) NOT NULL,
CONSTRAINT ck_order_items_unit_price CHECK (unit_price >= 0),
CONSTRAINT ck_order_items_quantity CHECK (quantity > 0)
);

使用精确数值并不代表可以忽略舍入:税率、折扣和分摊仍需先约定计算顺序、保留位数和最终舍入时机。

近似数值:REAL、FLOAT 和 DOUBLE PRECISION​

近似数值通常以科学计数法的二进制形式保存。它们能表示很大的范围,但只有有限的有效数字,因此:

-- 结果可能不是精确的十进制 0.3
SELECT CAST(0.1 AS DOUBLE PRECISION)
+ CAST(0.2 AS DOUBLE PRECISION);

近似类型适合误差本来就存在的场景,例如传感器读数、物理测量和统计模型。不要使用相等比较判断浮点计算结果:

-- 根据业务容差判断,而不是直接写 value = 0.3
WHERE ABS(measured_value - :expected_value) < :epsilon

FLOAT(p) 中的 p 在不同数据库里可能表示精度、存储大小或被直接忽略,不能据此推断跨数据库一致的有效位数。REAL 和 DOUBLE PRECISION 的位数也以目标数据库定义为准。

NULL、特殊值与符号​

  • NULL 表示缺失或未知,不是 0,也不是“没有小数”。参与算术运算时,含 NULL 的结果通常仍为 NULL。
  • 0 是有效数值,表示数量为零;是否允许负数应由业务规则和 CHECK 约束决定。
  • NaN、正负无穷等特殊值只在部分数据库或浮点实现中可用,排序、比较、索引和跨系统传输行为可能不同。除非确认目标系统完整支持,否则不要把它们当作通用 SQL 能力。

类型转换与混合运算​

数据库可能自动把不同数值类型转换成共同类型,但隐式转换规则依数据库而异,也可能导致精度丢失或索引无法使用。重要计算应显式转换:

SELECT
CAST(total_amount AS DECIMAL(14, 2)) / NULLIF(item_count, 0) AS average_amount
FROM daily_sales;

这里 NULLIF(item_count, 0) 避免除数为零;返回 NULL 后,应用可以按业务需要展示为“暂无数据”。CAST 的目标精度必须足够容纳中间结果,而不是只够容纳最终展示值。

整数除法尤其容易造成误解:有些数据库中两个整数相除仍得到整数,有些会得到带小数的结果。需要小数结果时,显式把至少一个操作数转换为精确小数或近似数值,并用测试确认结果:

SELECT CAST(completed_count AS DECIMAL(12, 4)) / total_count
FROM task_summary
WHERE total_count > 0;

如何选择数值类型​

可以按以下顺序判断:

  1. 是否参与算术运算? 不参与运算的编码、电话和邮编通常用字符类型。
  2. 是否必须十进制精确? 金额、账务、可对账的比例使用 DECIMAL/NUMERIC。
  3. 是否允许测量误差? 科学测量和模型结果可考虑 REAL 或 DOUBLE PRECISION。
  4. 最大值和最小值是多少? 结合当前值、增长速度和中间结果选择 INTEGER 或 BIGINT 等范围。
  5. 需要多少小数位? 根据业务规则设置 s,同时为整数部分和计算过程预留空间。
  6. 是否跨数据库? 优先使用通用类型,避免依赖无符号整数、特定自动增量或非标准别名。

一个常见的表设计可以是:

CREATE TABLE products (
product_id BIGINT NOT NULL,
stock INTEGER NOT NULL DEFAULT 0,
price DECIMAL(12, 2) NOT NULL,
weight_kg DOUBLE PRECISION,
CONSTRAINT pk_products PRIMARY KEY (product_id),
CONSTRAINT ck_products_stock CHECK (stock >= 0),
CONSTRAINT ck_products_price CHECK (price >= 0),
CONSTRAINT ck_products_weight CHECK (weight_kg IS NULL OR weight_kg >= 0)
);

这里库存是可计数的整数,价格需要十进制精确,重量允许测量误差;每个字段的约束进一步表达了可接受范围。

常见误区​

把所有数字都设成 INT​

这会丢失小数,或让大规模计数在增长后溢出。先看业务含义和范围,再选类型。

用浮点数保存金额​

显示为两位小数不代表内部计算精确。金额应使用 DECIMAL/NUMERIC,并明确舍入规则。

只给最终列预留精度​

price * quantity、汇总和税费计算的中间结果可能比最终展示值更大或需要更多小数位。应按整个计算链路规划精度。

依赖隐式转换​

隐式转换可能改变除法结果、触发舍入、导致索引失效,甚至在不同数据库中得到不同结果。边界和跨类型运算尽量显式 CAST。

把 NULL 当作零​

“未知金额”和“金额为零”通常是不同业务状态。聚合函数对 NULL 的处理也不同,例如 COUNT(column) 不统计该列为 NULL 的行。

小结​

  • 整数适合计数和标识,先评估范围再选择 INTEGER 或 BIGINT。
  • DECIMAL(p, s)/NUMERIC(p, s) 适合金额等必须精确的十进制数据;p 是总位数,s 是小数位数。
  • REAL、FLOAT、DOUBLE PRECISION 是近似数值,适合允许误差的测量和科学计算。
  • 明确处理 NULL、除零、舍入、溢出和中间结果精度,并对目标数据库的方言行为做边界测试。