PostgreSQL 调用 round 报 double precision, integer 函数不存在
PostgreSQL 的双参数 round 要求 numeric,浮点计算结果需在函数内部转换。用 pg_typeof 核对类型,并区分转换前后的运算精度和整数除法。
在查询中给计算结果保留两位小数,写成 round(calculated_value, 2),PostgreSQL 却返回:
ERROR: function round(double precision, integer) does not exist
Hint: No function matches the given name and argument types.
You might need to add explicit type casts.
这条错误已经指出两个实参的类型:第一个是 double precision,第二个是 integer。PostgreSQL 有接收单个浮点数的 round,但指定小数位数的双参数形式要求第一个实参是 numeric。
本文针对 PostgreSQL 内置函数的这组类型不匹配;相关签名在 PostgreSQL 15 和 18 中一致。DBeaver 等客户端显示这条数据库错误时,应先检查 SQL 表达式类型,不需要为此重装客户端或修改所有参与计算的列。
看计算结果的类型,不只看表字段
同一个函数名可以有不同的参数组合。三种相关签名如下:
| 调用形式 | 含义 | 返回类型 |
|---|---|---|
round(double precision) |
浮点数取最近整数 | double precision |
round(numeric) |
十进制数取最近整数 | numeric |
round(numeric, integer) |
按指定小数位数舍入 | numeric |
例如,输入列是 double precision,经过 sum 后仍返回 double precision;avg 接收 real 或 double precision 时也返回 double precision。即使最后显示出来像整数,也不代表它已经变成整数类型。
可以用 pg_typeof 查看传给 round 之前的整个表达式。下面是独立的只读 SQL 示例,不依赖业务表:
WITH totals(total_value, item_count) AS (
VALUES (21.5::double precision, 2::smallint)
)
SELECT
total_value / item_count AS calculated_value,
pg_typeof(total_value / item_count) AS calculated_type
FROM totals;
按这些输入类型,商的类型是 double precision。放回实际查询时,保留原来的 CTE、连接和分组,临时把最终的 round(...) 换成对同一表达式的 pg_typeof(...)。只检查分子或某个基础列,可能漏掉聚合与除法带来的类型变化。
如果错误显示的是数组、文本或其他类型,处理路径就不同。不要看到函数不存在便统一追加 ::numeric,应先确认输入确实是要舍入的单个数值。
在进入双参数 round 之前转换
若目标是把已有浮点计算结果舍入到两位小数,转换应放在 round 的第一个参数内部:
WITH totals(total_value, item_count) AS (
VALUES (21.5::double precision, 2::smallint)
)
SELECT round(
CAST(total_value / item_count AS numeric),
2
) AS rounded_value
FROM totals;
这个示例按数值规则应得到 10.75。转换只影响查询表达式,不修改表结构或原始数据。
PostgreSQL 也支持双冒号写法。下面与上面的 CAST 位置相同:
WITH totals(total_value, item_count) AS (
VALUES (21.5::double precision, 2::smallint)
)
SELECT round((total_value / item_count)::numeric, 2) AS rounded_value
FROM totals;
双冒号必须完整写成 ::;一个冒号不是 PostgreSQL 的这种类型转换语法。若不熟悉缩写,直接使用 CAST(... AS numeric) 更容易核对括号和转换范围。
将转换写到 round 外层,例如 round(calculated_value, 2)::numeric,不能解决问题:数据库仍然要先找到并调用 round(double precision, integer),而这组内置签名不存在。
删除第二个参数虽然可能让函数匹配成功,却会把结果舍入成整数,改变了“保留两位小数”的要求。也没有必要为了匹配函数把计数列改成小数类型;计数本来就可以保持整数,所需转换发生在待舍入的数值表达式上。
转换位置还会影响计算精度
给最终结果加 ::numeric,解决的是函数参数匹配。如果前面的求和、平均或除法已经采用浮点运算,这一步不会把之前的近似结果还原成最初输入的精确十进制数。
需要在十进制类型中完成聚合时,可以在聚合之前转换。下面用于比较两个转换位置,不代表两列结果在所有数据上都相同:
WITH samples(value) AS (
VALUES (10.25::double precision), (11.25::double precision)
)
SELECT
round(CAST(sum(value) AS numeric), 2) AS cast_after_sum,
round(sum(CAST(value AS numeric)), 2) AS cast_before_sum
FROM samples;
左边先按浮点类型求和,再转换;右边先转换每个输入,再按 numeric 求和。若数据最初已经存为浮点数,右边也无法恢复入库前丢失的信息。业务要求精确十进制存储和计算时,需要单独评估输入、字段类型及完整运算链,而不能把一次查询转换当成数据精度迁移。
整数除法还有另一种顺序问题:
SELECT
round(CAST(5 / 2 AS numeric), 2) AS cast_after_division,
round(CAST(5 AS numeric) / 2, 2) AS cast_before_division;
第一项先做整数除法,得到 2 后再转换,不能补回已经丢掉的小数部分;第二项在除法前把分子转换成 numeric,才会按十进制数计算 2.5。这里是帮助核对运算顺序,不是说报错中的 double precision 一定由整数除法产生。
舍入也不等于截断。numeric 的 round 在恰好处于中点时向远离零的方向舍入;如果业务要求直接截去多余小数,应另行确认是否使用 trunc,修复类型错误时应保留原定的舍入规则。
核对返回类型、边界值和调用方
修改实际查询时,可以暂时同时返回原始值、舍入值和舍入后的类型。以下仍用相同的独立数据示例:
WITH totals(total_value, item_count) AS (
VALUES (21.5::double precision, 2::smallint)
)
SELECT
total_value / item_count AS raw_value,
round(CAST(total_value / item_count AS numeric), 2) AS rounded_value,
pg_typeof(
round(CAST(total_value / item_count AS numeric), 2)
) AS rounded_type
FROM totals;
双参数形式返回 numeric。验收时除了确认不再出现 42883,还应检查正数、负数、舍入中点,以及原查询中的空值和零分母处理。转换类型不会让零分母自动变得合法,相关业务规则仍需明确。
客户端将数字显示成几位小数,与数据库是否正确舍入是两个问题。如果 SQL 已返回预期数值,但界面省略了尾随零,应检查客户端或应用的显示格式,不要继续改写运算来补界面格式。调用方若原本按浮点数读取结果,也要确认它能正确接收 numeric。
这次修复可以只改查询中的类型转换,保留原 SQL 便于回退,不需要修改已有列或创建同名自定义函数。回退会同时恢复旧查询行为;若旧查询本就报错,应恢复到已知可用的查询版本,再单独处理舍入要求。


