psycopg2保存数据报错:NUMERIC(4,3)字段精度不符合要求
解决Postgres NUMERIC(4,3)与SQLAlchemy Float(3)的数值保存异常问题
问题场景
Postgres表中字段定义为NUMERIC(4,3),SQLAlchemy模型里用Column(Float(3))做映射。用Python的round()处理数值(比如0.034)后保存,会抛出错误:
"A field with precision 4, scale 3 must round to an absolute value less than 10^1"
但直接用SQL的ROUND()函数插入同数值却完全正常。
原因解析
- 类型不匹配:SQLAlchemy的
Float(3)映射的是Postgres的FLOAT类型,而非表定义的NUMERIC。当Python浮点数(哪怕是round后的结果,比如0.034实际可能是二进制浮点数的近似值0.034000000000000002)传到psycopg2时,会触发FLOAT到NUMERIC的隐式转换,Postgres在转换时会因浮点数精度误差误判数值超出范围。 - 字段规则明确:
NUMERIC(4,3)要求总位数4位、小数位3位,整数部分最多1位,合法取值范围是-9.999到9.999。SQL的ROUND()返回的是NUMERIC类型,和字段类型完全匹配,不会有转换偏差。
解决方案
1. 修正模型字段类型(核心)
把SQLAlchemy中的Float(3)替换为Numeric(4, 3),让ORM字段和数据库字段类型完全对应:
from sqlalchemy import Numeric @dataclass class TableName(TableBase): __tablename__ = 't_cal_table_name' __table_args__ = ([constraints_and_stuff]) # ...其他字段... first_second_ratio = Column(Numeric(4, 3), nullable=False) second_double_fault_ratio = Column(Numeric(4, 3), nullable=False) # ...其他字段...
2. 用Decimal处理数值(避免浮点数误差)
如果需要在Python中计算并格式化数值,改用decimal.Decimal代替float,彻底消除二进制浮点数的精度问题:
from decimal import Decimal, ROUND_HALF_UP # 用Decimal进行除法计算 first_second_ratio = Decimal(first_serve_sum) / Decimal(second_serve_sum) # 四舍五入到3位小数 first_second_ratio = first_second_ratio.quantize(Decimal('0.000'), rounding=ROUND_HALF_UP)
3. 前置数值范围校验
保存前手动检查数值是否在合法范围内,提前拦截异常:
def validate_ratio(value): if not (-9.999 <= value <= 9.999): raise ValueError(f"数值{value}超出NUMERIC(4,3)范围") return value first_second_ratio = validate_ratio(round(first_serve_sum / second_serve_sum, 3))
内容的提问来源于stack exchange,提问作者Tamás Király
相关产品推荐
相关产品推荐

