花拾录
← 返回知识库

原生查询返回的整数是 BigInt,直接丢进 in 筛选就报错

数据库导入2026/09/220 阅读0 评论

你用 ORM 的原生查询搜了一批记录,拿到 id 列表,顺手拼成 in [...] 去做第二次查询。结果报错,说类型不对。明明打印出来就是数字啊。

现象

用原生查询拿到的结果,拼成一个 in [...] 查询时报错:

TypeError: Do not know how to serialize a BigInt

或者:

Cannot mix BigInt and other types, use explicit conversions

打印这些值的时候,console.log 显示的就是普通的数字,看不出任何异常:

const rows = await prisma.$queryRaw`select id from article where ...`;
console.log(rows);   // [ { id: 1 }, { id: 2 }, { id: 3 } ]  ← 看起来完全正常

这是这个坑最迷惑的地方:肉眼看不出问题,但类型就是不对。

还有一个常见的触发点:把原生查询的结果直接 JSON.stringify 返回给前端,或者交给日志库序列化——这些地方都会与 BigInt 冲突,而报错位置离真正的源头很远。

更坑的是错误的位置:报错往往不发生在"取数据"的那一行,而是发生在"用数据"的地方——比如对象序列化、算术运算、或者拼进查询参数时。于是你会盯着出错的那行看半天,而那行其实没问题,问题在更早的地方埋着。

根因

原生查询返回的整数,在客户端里是 BigInt,不是普通 number。

JavaScript 里 Number 是 IEEE 754 双精度浮点,安全整数范围只有 ±2^53-1。数据库的 BIGINT(64 位整数)超出这个范围时无法精确表示,所以驱动把这类整数值映射成了 BigInt 类型。

BigInt 和 number 是两种不同的类型,混用会报错:

typeof 1n   // 'bigint'
typeof 1    // 'number'

1n + 1      // TypeError: Cannot mix BigInt and other types
JSON.stringify(1n)  // TypeError: Do not know how to serialize a BigInt

为什么打印出来像数字? 因为 console.log 的显示形式里 1n 和 1 看起来几乎一样(有些环境下 BigInt 带个 n 后缀,有些格式化后完全看不出来)。

为什么只有原生查询出问题? 因为 ORM 对"托管查询"和"原生查询"的类型映射策略不同——通常模型字段上的 Int 会映射成 number(有模型元信息可以参考),但原生 SQL 查询(raw query)的结果没有模型元信息,驱动只能按数据库的实际类型来,于是变成 BigInt。

换句话说:托管查询有"模型的类型声明"这个上下文,驱动知道该往 number 转;原生查询没有这个上下文,驱动只能保守地按 BIGINT 处理。

解决

做法一:把取回的值转成 Number 再传入。

const rows = await prisma.$queryRaw`select id from article where ...`;
const ids = rows.map(r => Number(r.id));   // BigInt → number

await prisma.post.findMany({ where: { id: { in: ids } } });

做法二:如果值可能超出 Number 的安全范围,不要简单地 Number()(会丢精度),而是把比较条件也改成 BigInt,或者干脆在 SQL 层面用子查询 / join 完成,避免把值取回应用层再传回去:

// 用子查询,不经过应用层,天然没有类型转换问题
const rows = await prisma.$queryRaw`
  select * from post
  where "articleId" in (select id from article where ...)
`;

这是更根本的做法——当两边的查询都在数据库里时,类型根本不需要跨语言边界。

做法三:给 ORM 客户端配置类型映射,让它在原生查询里也返回 number。前提是业务上确认这些值不会超出安全整数范围(比如自增主键,实际用量远小于 2^53)。

延伸与预防

这条的通用教训:跨语言 / 跨驱动的"整数",类型未必是同一个。

具体建议:

一、在做 in 筛选、JSON 序列化、或者算术前,先想一下"这个值的类型到底是什么"。 用 typeof 打印,而不是用 console.log 肉眼判断:

console.log(typeof r.id, r.id);   // 'bigint' 1n

二、遇到"打印出来挺正常的但就是报类型错",第一反应就怀疑 BigInt / 隐式类型转换。 这个症状组合很有辨识度。

三、项目里加一个小的工具函数统一处理这类转换,别在每个调用点各写一遍:

const toNum = v => (typeof v === 'bigint' ? Number(v) : v);

顺便记住一个相关的坑:JSON 序列化遇到 BigInt 会直接抛错,所以任何要往前端传的数据里如果有 BigInt,也必须先转换——否则接口会在返回时炸掉,而且错误堆栈可能指向序列化层,不指向你的业务代码。

再补一个容易忽略的连带影响:BigInt 不只影响传参,还会影响比较和排序。除了 in 之外,像 >、<、排序这类操作一旦混入不同类型的值,也可能出现意料之外的结果——要么抛错,要么比较行为不符合直觉。统一成同一种类型(全部 number,或全部 BigInt)是最省心的做法。

评论(0)

  • 还没有评论,来抢沙发~

相关文章