Postgres:使用 NULLIF 时出现“...超出整数类型的范围”

Postgres: getting "... is out of range for type integer" when using NULLIF

对于上下文,这个问题发生在我使用默认的 postgres 数据库驱动程序编写的 Go 程序中。

我一直在构建与 postgres 数据库对话的服务,该数据库具有 table 类似于下面列出的数据库:

CREATE TABLE object (
    id SERIAL PRIMARY KEY NOT NULL,
    name VARCHAR(255) UNIQUE,
    some_other_id BIGINT UNIQUE
    ...
);

我已经为这个项目创建了一些端点,包括一个“安装”端点,它有效地充当一个 upsert 函数,如下所示:

INSERT INTO object (name, some_other_id)
VALUES (, )
ON CONFLICT name DO UPDATE SET
    some_other_id = COALESCE(NULLIF(, 0), object.some_other_id)

我还有一个“更新”端点,其底层查询如下:

UPDATE object
SET some_other_id = COALESCE(NULLIF(, 0), object.some_other_id)
WHERE name = 

问题:

每当我 运行 更新查询时,我总是 运行 进入错误,引用字段 "some_other_id":

pq: value "1010101010144" is out of range for type integer

然而,这个错误永远不会发生在查询的“upsert”版本上,即使该行已经存在于数据库中(当它必须计算 COALESCE 语句时)。我已经能够通过将 COALESCE 语句更新为如下所示来防止此错误:

COALESCE(NULLIF(, CAST(0 AS BIGINT)), object.some_other_id)

但由于第一次查询时从未发生过这种情况,我想知道这种不一致是因为我做错了什么还是我不明白?还有这方面的最佳做法是什么,我应该投射所有价值吗?

我肯定将 64 位整数传递给“some_other_id”的查询,即使没有显式类型转换,第一个查询也适用于 Go 实现。

如果需要更多信息(或 Go 实现),请告诉我,非常感谢! (:

编辑:

为了消除混淆,查询直接在 Go 代码中执行,如下所示:

res, err := s.db.ExecContext(ctx, `UPDATE object SET some_other_id = COALESCE(NULLIF(, 0), object.some_other_id) WHERE name = `,
    "a name",
    1010101010144,
)

两个查询的执行方式完全相同。

编辑: 还更正了我当前解决方法中的参数(从 </code> 到 <code>)。

我还想借此机会指出,查询确实适用于我建议的修复,这表明问题出在我混淆了 postgres 和 NULLIF 语句中的类型?在我的代码和数据库之间没有要求 INTEGER arg 的存储过程,至少我已经写过了。

COALESCE(NULLIF(, CAST(0 AS BIGINT)), object.some_other_id)

五十一?真的吗?

pq: value "1010101010144" is out of range for type integer

注意,报错信息中的数据类型是integer,不是bigint.

我认为错误的原因是显示的代码于是我拿出一个神奇的crystal球,用手一传

an "Install" endpoint which effectively acts as an upsert function like so

I also have an "Update" endpoint

您将端点称为 PostgreSQL function(存储过程)吗?我想是的。 另外 $1, $2 看起来像 PostgreSQL 函数参数。

神奇的 crystal 球说:你有两个具有不同数据类型参数的 PostgreSQL 函数:

  1. “安装”端点有 $2 函数参数作为 bigint 数据类型。看起来像 CREATE FUNCTION Install(VARCHAR(255), bigint)

  2. “更新”端点有 $2 函数参数作为 整数 数据类型,而不是 bigint。看起来像 CREATE FUNCTION Update(VARCHAR(255), integer).

最后,我会重写你的条件更容易理解:

UPDATE object
SET some_other_id = 
CASE 
WHEN  = 0 THEN object.some_other_id
ELSE 
END
WHERE name = 

这与 postgres 解析器如何解析参数类型有关。我不知道它究竟是如何实现的,但考虑到观察到的行为,我会假设 INSERT 查询不会失败,因为从 (name,some_other_id) VALUES (,) 可以清楚地看出 </code> 参数应该与目标 <code>some_other_id 列具有相同类型,其类型为 int8。此类型信息随后也用于查询 DO UPDATE SET 部分的 NULLIF 表达式。

您还可以通过在 INSERT 中使用 (name) VALUES () 来测试这个假设,您会看到 DO UPDATE SET 中的 NULLIF 表达式将以同样的方式失败正如在 UPDATE 查询中所做的那样。

所以 UPDATE 查询失败,因为解析器没有足够的上下文来推断 </code> 参数的准确类型。解析器可以用来推断 <code> 类型的“最接近”的东西是 NULLIF 调用表达式,特别是它使用调用表达式的第二个参数的类型,即 0 ,其类型为 int4,然后它使用该类型信息作为第一个参数,即 </code>.</p> <p>为避免此问题,您应该对无法准确推断类型的任何参数使用显式类型转换。即使用 <code>NULLIF(::int8, 0).