You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLAlchemy Float类型列filter/filter_by等值查询不生效问题

问题背景

对存储站点经纬度的表做等值过滤查询拦截重复数据时,数据库层==匹配浮点类型字段返回空结果,但将全量数据加载到内存中做等值判断可以正常匹配到目标记录。
复现代码、模型定义如下:

# 查询逻辑
data_dict = {'lat':33.7838, 'lng':-117.225}

filter_test = session.query(Sites.lat, Sites.lng).filter(
        and_(
            Sites.lat == data_dict['lat'],
            Sites.lng == data_dict['lng']
        )
    ).all()

print(f'Filter test: {filter_test}')
no_filter = session.query(Sites).all()
for rec in no_filter:
   if rec.lat == data_dict['lat']:
       print(rec.country, rec.lat, rec.lng)
       print(rec.lat == data_dict['lat'])    

# 输出结果
# 数据库层过滤输出:Filter test: []
# 内存遍历输出:
# United States 33.7838 -117.225
# True
# United States 33.7838 -117.225
# True

# 表模型定义
class Sites(Base):
    __tablename__ = "sites"
    get_table_name = get_table_name
    country = Column(String(15))
    zipcode = Column(Integer)
    lat = Column(Float)
    lng = Column(Float)
根本原因

问题出在浮点数精度不匹配:

  • SQLAlchemy中默认的Float类型映射到数据库的是单精度浮点数(FLOAT),仅占4字节,有效数字精度仅6~7位,存储时会对超出精度的数值做截断。
  • Python原生的float是双精度浮点数,占8字节,有效数字精度为15~17位。
  • 数据库层做==等值匹配时,传入的双精度值和库中存储的截断后的单精度值存在微小的精度差,判断为不相等,因此返回空结果。
  • 全量数据拉到内存后,SQLAlchemy会将数据库读出的单精度值转换为Python双精度float类型,截断后的数值和定义的常量值在Python的浮点比较逻辑下判定为相等,因此内存遍历可以匹配到结果。
修复方案
  • 优先使用定点数类型存储经纬度:将字段类型改为Numeric/DECIMAL定点类型,指定固定精度(经纬度保留6位小数即可达到米级定位精度,完全满足站点去重需求),从存储层面消除浮点精度误差,支持精确等值查询。
    from sqlalchemy import Numeric
    
    class Sites(Base):
        __tablename__ = "sites"
        get_table_name = get_table_name
        country = Column(String(15))
        zipcode = Column(Integer)
        # 总长度10位,小数占6位
        lat = Column(Numeric(precision=10, scale=6))
        lng = Column(Numeric(precision=10, scale=6))
    
  • 保留浮点类型时改用范围查询:如果不想修改字段类型,不要对浮点字段做严格等值判断,增加一个极小的误差容忍区间做范围匹配即可,经纬度场景取1e-6的容忍度(对应地表约10厘米误差)足够覆盖精度偏差。
    error_tolerance = 1e-6
    filter_test = session.query(Sites.lat, Sites.lng).filter(
        and_(
            Sites.lat.between(data_dict['lat'] - error_tolerance, data_dict['lat'] + error_tolerance),
            Sites.lng.between(data_dict['lng'] - error_tolerance, data_dict['lng'] + error_tolerance)
        )
    ).all()
    
  • 对齐浮点精度:将字段类型改为Double双精度浮点类型,和Python原生float精度对齐,可以减少绝大多数精度不匹配问题,但依然不建议对任何浮点类型做严格等值判断,存在出现偶发精度偏差的可能。
    from sqlalchemy import Double
    
    class Sites(Base):
        __tablename__ = "sites"
        get_table_name = get_table_name
        country = Column(String(15))
        zipcode = Column(Integer)
        lat = Column(Double)
        lng = Column(Double)
    

内容的提问来源于stack exchange,提问作者Ian Murray

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:48:21