【BUG】PostgreSQL ERROR invalid input syntax for type numeric XXXX

PostgreSQL ERROR: invalid input syntax for type numeric: "XXXX"

问题描述

最近在项目中遇到了一个诡异的 SQL 查询错误,同样的查询结构,只是参数不同,有的能正常执行,有的却报错

错误现象

sql 复制代码
-- 查询1:正常执行
SELECT * FROM order_product_rela 
WHERE order_id = 321 AND product_id = 2113;

-- 查询2:报错
SELECT * FROM order_product_rela 
WHERE order_id = 334 AND product_id = 2216;
-- ERROR: invalid input syntax for type numeric: "PRODUCT_A"

两个查询的结构完全相同,只是参数不同,为什么一个正常一个报错?

背景

order_product_rela 表是订单产品关联表,product_id 字段最初设计为 int8 类型,只存储产品ID。后来业务需求变更,需要同时支持产品ID和产品编码,于是将字段类型改为 varchar,兼容了之前的数字ID和新的字母编码。(注:不建议这样设计表,这是之前别人设计的)

问题分析

查看执行计划

首先,我查看了查询的执行计划:

sql 复制代码
EXPLAIN SELECT * FROM order_product_rela 
WHERE order_id = 334 AND product_id = 2216;

执行计划如下:

复制代码
QUERY PLAN                                                               | 
 -------------------------------------------------------------------------+ 
 Seq Scan on order_product_rela  (cost=0.00..4.12 rows=3 width=415)   | 
   Filter: ((order_id = 334) AND ((product_id)::bigint = 2216))         |

关键信息:

  • Seq Scan:全表扫描,没有使用索引
  • Filter :(product_id)::bigint = 2216,PostgreSQL 将 product_id 字段转换为 bigint 类型进行比较

为什么会报错?

由于是全表扫描,PostgreSQL 需要对每一行数据执行过滤条件:

对于每一行数据:

  1. 检查 order_id = 334
  2. 如果满足,则检查 product_id = 2216
  3. 在检查 product_id 时,需要将字段值转换为 bigint
  4. 如果遇到 "PRODUCT_A" 这样的字符串,转换失败,报错

为什么查询1不报错?

sql 复制代码
SELECT * FROM order_product_rela 
WHERE order_id = 321 AND product_id = 2113;

这个查询不报错的原因是:

  • order_id = 321 的行中,product_id 都是数字字符串(如 "2113")
  • 类型转换成功:CAST("2113" AS bigint) = 2113 ✅
  • 所有类型转换都成功,查询正常执行

为什么查询2报错?

sql 复制代码
SELECT * FROM order_product_rela 
WHERE order_id = 334 AND product_id = 2216;

这个查询报错的原因是:

  • order_id = 334 的行中,存在 product_id = "PRODUCT_A" 的数据
  • 类型转换失败:CAST("PRODUCT_A" AS bigint) = 2216 ❌
  • 遇到无法转换的值,查询报错

PostgreSQL 的类型转换机制

隐式类型转换

当 SQL 查询中字段类型和参数类型不匹配时,PostgreSQL 会尝试进行隐式类型转换:

sql 复制代码
-- product_id 是 varchar 类型,参数 2216 是数字类型
WHERE product_id = 2216

-- PostgreSQL 可能会尝试:
1. 将参数转换为字符串:product_id = '2216'  ✅ 推荐
2. 将字段值转换为数字:CAST(product_id AS bigint) = 2216  ❌ 可能失败

PostgreSQL 的类型转换规则:

  • 如果没有明确的类型转换规则,可能会选择将字段值转换为参数类型
  • 这就导致了将 "PRODUCT_A" 转换为数字时报错

短路求值(Short-circuit Evaluation)

PostgreSQL 在执行 WHERE 条件时,会采用短路求值策略:

sql 复制代码
WHERE order_id = 334 AND product_id = 2216

执行过程(对每一行数据):

复制代码
第1行:order_id = 321
  ↓
检查 order_id = 334? → false
  ↓
停止,不检查 product_id(短路)

第2行:order_id = 334, product_id = "2216"
  ↓
检查 order_id = 334? → true
  ↓
检查 product_id = 2216? → 需要类型转换
  ↓
CAST("2216" AS bigint) = 2216 → true

第3行:order_id = 334, product_id = "PRODUCT_A"
  ↓
检查 order_id = 334? → true
  ↓
检查 product_id = 2216? → 需要类型转换
  ↓
CAST("PRODUCT_A" AS bigint) = 2216 → ❌ 报错!

短路求值 = 从左向右执行 + 提前停止

  • 从左向右:按照条件在 SQL 中出现的顺序执行
  • 提前停止:当可以确定最终结果时,就不再检查后面的条件

解决方案

使用字符串参数,避免类型转换

相关推荐
ClickHouseDB3 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
m0_639906943 小时前
Redis入门|五种数据结构、使用场景、持久化RDB&AOF
数据结构·数据库·redis
时间售卖铺4 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
qetfw5 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
数智园区合作社5 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai
lbb 小魔仙6 小时前
Windows搭建PaddleOCR+飞书Webhook自动打卡通知机器人:识别、推送与远程触发完整教程
数据库·人工智能·prometheus·时序数据库
captain3767 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
心中有你02147 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
龙亘川8 小时前
综合素质评价业务建模:从过程性评价到成长档案全链路闭环
数据库·智慧城市·数据可视化
数据库小组8 小时前
NineData 实战教程:MySQL 5.5 数据迁移同步到 MySQL 8.0(跨版本升级)
数据库·mysql·mysql优化·数据库开发·数据库迁移·版本升级·数据复制