Postgres 13

问题描述

循环调用错误

我一直被 Postgres 中的 round() 绊倒年龄,我想我最终会尝试找出规则。这是我经常遇到的那种错误

ERROR:  function round(double precision,integer) does not exist.

这可能对其他人有意义,但让我感到困惑。尽我所能,如果您想要带有小数的四舍五入结果,您必须强制转换为 decimal/numeric。这是记录在案的行为,很难理解为什么 realdouble 不能四舍五入。我在下面有更多详细信息,但总而言之,我已经为 rounddouble precision 列和计算编写了 real

总体发现/印象

  • 整个整数类型四舍五入到最接近的整数,正如您所猜测的那样。

  • 但是,如果您将一些舍入小数传递给整数除法,则没有错误

  • decimalnumeric 很好。 (正如人们所知,它们在内部是一样的。)

  • realdouble precision 不受支持。我不明白为什么,这些似乎是您自然想要舍入的值。

  • serial 类型不受支持,会引发“未知类型”错误。我倾向于认为这是我的测试设置中的错误,但我看不到哪里。而且,不,我无法想象需要舍入串行类型,但我希望它们的行为与它们的整数对应物一样。

  • 分部显式转换为

这是否正确且完整?

自定义函数

如果有人能帮助我更轻松地理解 round 的行为原因,并为我指出比这对函数更好的解决方案,我将不胜感激:

/*
OVERLOADING: Not my thing but,in this case,sensible.
I'm not sure if the real version is required or not.
*/

------------------------------------
-- double precision version
------------------------------------
CREATE OR REPLACE FUNCTION tools.round(
     value_to_round   double precision,round_to         integer  default 0)

    RETURNS double precision

-- Note: Not validating that the number of places in non-negative. Don't do that.

AS $BODY$

SELECT round(value_to_round::numeric,round_to)::double precision;

$BODY$
  LANGUAGE sql;

------------------------------------
-- real version
------------------------------------
-- I don't think that this is ever invoked. I have only managed to get the double version to run.

CREATE OR REPLACE FUNCTION tools.round(
     value_to_round   real,round_to         integer  default 0)

    RETURNS real

-- Note: Not validating that the number of places in non-negative. Don't do that.

AS $BODY$

SELECT round(value_to_round::numeric,round_to)::real;

$BODY$
  LANGUAGE sql;

测试代码

为了测试这一点,我获取了数字类型 https://www.postgresql.org/docs/current/datatype-numeric.html 的列表,并对每个类型进行了类似的测试,一次一个

-- Test 1: SMALLINT
-- Result: smallint,integer,0
SELECT 'smallint' as cast_type,pg_typeof(1/3::smallint) as result_type,1/3::smallint as raw_result,round(1/3::smallint,4);

-- Test 2: INTEGER
-- Result: integer,0
SELECT 'integer' as cast_type,pg_typeof(1/3::integer) as result_type,1/3::integer as raw_result,round(1/3::integer,4);

-- Test 3: BIGINT
-- Result: bigint,bigint,0
SELECT 'bigint' as cast_type,pg_typeof(1/3::bigint) as result_type,1/3::bigint as raw_result,round(1/3::bigint,4);

-- Test 4: DECIMAL
-- Result: decimal,numeric,0.33333333333333333333 and 0.3333
SELECT 'decimal' as cast_type,pg_typeof(1/3::decimal) as result_type,1/3::decimal as raw_result,round(1/3::decimal,4);

-- Test 5: NUMERIC
-- Result: 0.33333333333333333333 and 0.3333
-- Result: numeric,0.33333333333333333333 and 0.3333
SELECT 'numeric' as cast_type,pg_typeof(1/3::numeric) as result_type,1/3::numeric as raw_result,round(1/3::numeric,4);

-- Test 6: REAL
-- Result: ERROR:  function round(double precision,integer) does not exist.
-- Note : You MAY all round for whole number division round(1/3::real),the error is only on rounding to a decimal place.
-- Note: If you check the pg_typeof(1/3::real),it's double precision,not real.
SELECT 'real' as cast_type,pg_typeof(1/3::REAL) as result_type,1/3::REAL as raw_result,round(1/3::real,4);

-- Test 7: DOUBLE PRECISION
-- Result: ERROR:  function round(double precision,integer) does not exist
-- Note : You MAY all round for whole number division round(1/3::double precision),the error is only on rounding to a decimal place.
SELECT 'double precision' as cast_type,pg_typeof(1/3::double precision) as result_type,1/3::double precision as raw_result,round(1/3::double precision,4);

-- Test 8: SMALLSERIAL
-- Result: ERROR:  type "smallserial" does not exist. Eh? Wouldn't want to round a serial anyway but,?
SELECT 'smallserial' as cast_type,pg_typeof(1/3::smallserial) as result_type,1/3::smallserial as raw_result,round(1/3::smallserial,4);

-- Test 9: SERIAL
-- Result: ERROR:  type "serial" does not exist. Eh? Wouldn't want to round a serial anyway but,?
SELECT 'serial' as cast_type,pg_typeof(1/3::serial) as result_type,1/3::serial as raw_result,round(1/3::serial,4);

-- Test 10: BIGSERIAL
-- Result: ERROR:  type "bigserial" does not exist. Eh? Wouldn't want to round a serial anyway but,?
SELECT 'bigserial' as cast_type,pg_typeof(1/3::bigserial) as result_type,1/3::bigserial as raw_result,round(1/3::bigserial,4);

调查结果摘要

测试# 投射类型 1/3 结果类型 示例 四舍五入 原始结果 四舍五入的结果 错误
1 smallint 整数 round(1/3::real,4) 4 0 0
2 整数 整数 round(1/3::integer,4) 4 0 0
3 bigint bigint round(1/3::bigint,4) 4 0 0
4 十进制 数字 round(1/3::decimal,4) 4 0.333333333333333 0.3333
5 数字 数字 round(1/3::numeric,4) 4 0.333333333333333 0.3333
6 真实 双精度 round(1/3::real,4) 4 ERROR: function round(double precision,integer) does not exist.
6b 真实 双精度 round(1/3::real) 0 0
7 双精度 双精度 round(1/3::double precision,4) 4 ERROR: function round(double precision,integer) does not exist.
7b 双精度 双精度 round(1/3::double precision) 0 0
8 smallserial n/a round(1/3::smallserial,4) 4 ERROR: type "smallserial" does not exist
9 串行 n/a round(1/3::serial,4) 4 ERROR: type "serial" does not exist.
10 bigserial n/a round(1/3::bigserial,4) 4 ERROR: type "bigserial" does not exist.

评论回复

我在下面收到了一些评论,所有这些评论都很有帮助。回顾过去关于黑客的话题可以提供一些见解。我从它们中得出的结论是,人们已经发现这令人困惑十多年了。我真的很喜欢 Postgres,但它倾向于支持正确性而不是清晰度.

就其价值而言,这主要是因为我的老板遇到了“奇怪的错误”并问我 Postgres 的 WTF 错误。我不得不说,我真的无法提供任何他认为有用的答案,但我可以通过覆盖认行为来修复它。

我并不是说 Postgres 有错误,但这里的设计选择是......没有帮助。对于那些在 Postgres 工作了很多年和/或深入了解内部结构的人来说,可能很容易忘记局外人或新手可能会经历什么。我在 Postgres(远不是我的第一个数据库努力工作了四年多,但我仍然觉得自己是个初学者。对于那些对 Postgres 不太关注的人来说,令人困惑的行为只会无人问津。或者被认为是一个错误。 (其他语言和数据库对如何处理各种数字类型的舍入做出不同的选择。)

回到细节:

  • 当然,您不能更改固定精度十进制类型的精度。但是您可以将“四舍五入”的位置转换为 0。这绝对足够了。

  • 后来我想“也许 trunc 会起作用”,对于我们处理的数据质量来说,这当然足够了。它的行为与 round 相同。

据我所知,我的琐碎功能可以很好地完成我们的工作,并避免无益的错误。因此,我在我们的系统中重载了 roundtrunc 以按照我们想要的方式行事,并且永远不必再考虑这个问题。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)