问题描述
循环调用错误
我一直被 Postgres 中的 round()
绊倒年龄,我想我最终会尝试找出规则。这是我经常遇到的那种错误:
ERROR: function round(double precision,integer) does not exist.
这可能对其他人有意义,但让我感到困惑。尽我所能,如果您想要带有小数的四舍五入结果,您必须强制转换为 decimal/numeric
。这是记录在案的行为,很难理解为什么 real
和 double
不能四舍五入。我在下面有更多详细信息,但总而言之,我已经为 round
和 double precision
列和计算编写了 real
。
总体发现/印象
-
整个整数类型四舍五入到最接近的整数,正如您所猜测的那样。
-
但是,如果您将一些舍入小数传递给整数除法,则没有错误。
-
decimal
和numeric
很好。 (正如人们所知,它们在内部是一样的。) -
real
和double 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
相同。
据我所知,我的琐碎功能可以很好地完成我们的工作,并避免无益的错误。因此,我在我们的系统中重载了 round
和 trunc
以按照我们想要的方式行事,并且永远不必再考虑这个问题。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)