(((这是一个 Wiki!请编辑以增强!))
还可以尝试使用旧语法进行转换,
SELECT ROUND( AVG(some_column)::numeric, 2 ) FROM table;
适用于任何版本的 PostgreSQL。
...但是,作为最终解决方案,您可以重载ROUND 函数。
重载作为铸造策略
CREATE FUNCTION ROUND(float,int) RETURNS NUMERIC AS $f$
SELECT ROUND( CAST($1 AS numeric), $2 )
$f$ language SQL IMMUTABLE;
现在你的指令可以正常工作了,试试这个完整的比较:
SELECT trunc(n,3), round(n,3) n_round, round(f,3) f_round,
pg_typeof(n) n_type, pg_typeof(f) f_type, pg_typeof(round(f,3)) f_round_type
FROM (SELECT 2.0/3.0, 2/3::float) t(n,f);
截断 |
n_round |
f_round |
n_type |
f_type |
f_round_type |
0.666 |
0.667 |
0.667 |
数字 |
双精度 |
数字 |
ROUND(float , int)函数是f_round
,它返回一个(十进制)NUMERIC 数据类型,这对于某些应用程序来说很好:问题解决了!
在另一个应用程序中,我们也需要一个浮点数。另一种方法是使用round(f,3)::float
或创建一个round_tofloat()
函数。另一种选择,ROUND
再次重载函数,并使用浮点数的所有精度范围,是在定义精度时返回一个浮点数(参见IanKenney 的答案),
CREATE FUNCTION ROUND(
input float, -- the input number
accuracy float -- accuracy, the "counting unit"
) RETURNS float AS $f$
SELECT ROUND($1/accuracy)*accuracy
$f$ language SQL IMMUTABLE;
尝试
SELECT round(21.04, 0.05); -- 21.05 float!
SELECT round(21.04, 5::float); -- 20
SELECT round(1/3., 0.0001); -- 0.3333
SELECT round(2.8+1/3., 0.5); -- 3.15
SELECT round(pi(), 0.0001); -- 3.1416
PS:命令\df round
,在psql
重载后,将显示类似这个表的内容
架构 | 姓名 | 结果 | 争论
------------+-------+------------+------------------
架构 | 圆| 数字 | 浮点数,整数
架构 | 圆| 浮动 | 漂浮,漂浮
pg_catalog | 圆| 浮动 | 漂浮
pg_catalog | 圆| 数字 | 数字
pg_catalog | 圆| 数字 | 数字,整数
其中float是同义词,double precision
而myschema是public
当您不使用模式时。这些pg_catalog
函数是默认函数,请参阅指南内置数学函数。
舍入和成型
该to_char
函数在内部应用循环过程,因此,当您的目标只是在终端中显示最终结果时,您可以使用FM
修饰符作为数字格式模式的前缀:
SELECT round(x::numeric,2), trunc(x::numeric,2), to_char(x, 'FM99.99')
FROM (SELECT 2.0/3) t(x);
圆形的 |
截断 |
to_char |
0.67 |
0.66 |
.67 |
笔记
问题的原因
某些 PostgreSQL 函数缺少重载,为什么(???):我认为“这是一个不足”(!),但 @CraigRinger、@Catcall 和 PostgreSQL 团队同意“pg 的历史理由”。
关于性能和重用的注意事项
与直接强制转换编码相比,内置函数(例如 pg_catalog 的 ROUND)可以重载而不会造成性能损失。在实现高性能的用户定义转换函数时,必须采取两个预防措施:
结论:上面的ROUND(float,int)
函数,经过优化,比@CraigRinger的回答快多了;它将编译为(完全)相同的内部表示。因此,虽然它不是 PostgreSQL 的标准,但它可以成为您的项目的标准,通过一个集中且可重用的“片段库”,如 pg_pubLib。
四舍五入到第 n 位或其他数字表示
有些人认为 PostgreSQL 对 float 数据类型进行四舍五入是没有意义的,因为float 是二进制表示,它需要对位数或其十六进制表示进行四舍五入。
好吧,让我们解决这个问题,添加一个奇特的建议......这里的目的是在另一个重载函数中返回一个浮点
ROUND(float, text, int) RETURNS float
类型,即text
提供一个选择
'dec'
对于“十进制表示”,
'bin'
对于“二进制”表示和
'hex'
用于十六进制表示。
因此,在不同的表示中,我们对要四舍五入的位数有不同的解释。当d计算二进制数字而不是十进制或十六进制时,将数字x用一个近似较短的值舍入,用较少的“小数位”(tham 其原始d数字)将更短。
没有 C++,使用“纯 SQL”并不容易,但是这段代码片段将说明并且可以用作解决方法:
-- Looking for a round_bin() function! this is only a workaround:
CREATE FUNCTION trunc_bin(x bigint, t int) RETURNS bigint AS $f$
SELECT ((x::bit(64) >> t) << t)::bigint;
$f$ language SQL IMMUTABLE;
CREATE FUNCTION ROUND(
x float,
xtype text, -- 'bin', 'dec' or 'hex'
xdigits int DEFAULT 0
)
RETURNS FLOAT AS $f$
SELECT CASE
WHEN xtype NOT IN ('dec','bin','hex') THEN 'NaN'::float
WHEN xdigits=0 THEN ROUND(x)
WHEN xtype='dec' THEN ROUND(x::numeric,xdigits)
ELSE (s1 ||'.'|| s2)::float
END
FROM (
SELECT s1,
lpad(
trunc_bin( s2::bigint, CASE WHEN xd<bin_bits THEN bin_bits - xd ELSE 0 END )::text,
l2,
'0'
) AS s2
FROM (
SELECT *,
(floor( log(2,s2::numeric) ) +1)::int AS bin_bits, -- most significant bit position
CASE WHEN xtype='hex' THEN xdigits*4 ELSE xdigits END AS xd
FROM (
SELECT s[1] AS s1, s[2] AS s2, length(s[2]) AS l2
FROM (SELECT regexp_split_to_array(x::text,'\.')) t1a(s)
) t1b
) t1c
) t2
$f$ language SQL IMMUTABLE;
尝试
SELECT round(1/3.,'dec',4); -- 0.3333 float!
SELECT round(2.8+1/3.,'dec',1); -- 3.1 float!
SELECT round(2.8+1/3.,'dec'); -- ERROR, need to cast string
SELECT round(2.8+1/3.,'dec'::text); -- 3 float
SELECT round(2.8+1/3.,'dec',0); -- 3 float
SELECT round(2.8+1/3.,'hex',0); -- 3 float (no change)
SELECT round(2.8+1/3.,'hex',1); -- 3.1266
SELECT round(2.8+1/3.,'hex',3); -- 3.13331578486784
SELECT round(2.8+1/3.,'bin',1); -- 3.1125899906842625
SELECT round(2.8+1/3.,'bin',6); -- 3.1301821767286784
SELECT round(2.8+1/3.,'bin',12); -- 3.13331578486784
并且\df round
还有:
Schema | Name | Result | Argument
------------+-------+---------+---------------
myschema | round | float | x float, xtype text, xdigits int DEFAULT 0