postgresql:数据类型数字有限
postgresql: datatype numeric with limited digits
我正在寻找位数有限的数字数据类型
(小数点前后)
该函数只删除小数点后的数字。 (PG 版本 >= 13)
create function num_flex( v numeric, d int) returns numeric as
$$
select case when v=0 then 0
when v < 1 and v > -1 then trim_scale(round(v, d - 1 ) )
else trim_scale(round(v, d - 1 - least(log(abs(v))::int,d-1) ) ) end;
$$
language sql ;
用于测试:
select num_flex( 0, 6)
union all
select num_flex( 1.22000, 6)
union all
select num_flex( (-0.000000123456789*10^x)::numeric,6)
from generate_series(1,15,3) t(x)
union all
select num_flex( (0.0000123456789*10^x)::numeric,6)
from generate_series(1,15,3) t(x) ;
它运行,
但有人有更好的主意或发现错误(一种情况,未实现)?
下一步就是把这个集成到PG中,这样我就可以写了
select 12.123456789::num_flex6 ;
select 12.123456789::num_flex7 ;
对于具有 6 位或 7 位数字的 num_flex 数据类型。
类型从 num_flex2 到 num_flex9。这可能吗?
你的函数有一些问题:
- 接受负数计数(参数
d
)。 num_flex(1234,-2)
returns 1200
- 您指定您希望该函数仅删除小数点后的数字,因此 1234
是预期的。
- -1 和 1 之间的结果不正确。
num_flex(0.123,3)
returns 0.12
而不是 0.123
。我想如果你确实想在小数点左边计算 0,这也可能是你想要的效果。通常,当考虑数字的精度和小数位数时,0
会被忽略。
- 由于
::int
四舍五入的工作原理,您对小数点左侧数字的计数不正确。 log(abs(11))::int
是 1
但 log(abs(51))::int
是 2
。 ceil(log(abs(v)))::int
returns 2
在这两种情况下,同时保持 int
类型仍然作为 round()
. 中的第二个参数
create or replace function num_flex(
input_number numeric,
digit_count int,
is_counting_unit_zero boolean default false)
returns numeric as
$$
select trim_scale(
case
when input_number=0
then 0
when digit_count<=0 --avoids negative rounding
then round(input_number,0)
when (input_number between -1 and 1) and is_counting_unit_zero
then round(input_number,digit_count-1)
when (input_number between -1 and 1)
then round(input_number,digit_count)
else
round( input_number,
greatest( --avoids negative rounding
digit_count - (ceil(log(abs(input_number))))::int,
0)
)
end
);
$$
language sql;
这是一个测试
select *,"result"="should_be"::numeric as "is_correct" from
(values
('num_flex(0.1234 ,4)',num_flex(0.1234 ,4), '0.1234'),
('num_flex(1.234 ,4)',num_flex(1.234 ,4), '1.234'),
('num_flex(1.2340000 ,4)',num_flex(1.2340000 ,4), '1.234'),
('num_flex(0001.234 ,4)',num_flex(0001.234 ,4), '1.234'),
('num_flex(123456 ,5)',num_flex(123456 ,5), '123456'),
('num_flex(0 ,5)',num_flex(0 ,5), '0'),
('num_flex(00000.00000 ,5)',num_flex(00000.00000 ,5), '0'),
('num_flex(00000.00001 ,5)',num_flex(00000.00001 ,5), '0.00001'),
('num_flex(12345678901 ,5)',num_flex(12345678901 ,5), '12345678901'),
('num_flex(123456789.1 ,5)',num_flex(123456789.1 ,5), '123456789'),
('num_flex(1.234 ,-4)',num_flex(1.234 ,4), '1.234')
) as t ("operation","result","should_be");
-- operation | result | should_be | is_correct
----------------------------+-------------+-------------+------------
-- num_flex(0.1234 ,4) | 0.1234 | 0.1234 | t
-- num_flex(1.234 ,4) | 1.234 | 1.234 | t
-- num_flex(1.2340000 ,4) | 1.234 | 1.234 | t
-- num_flex(0001.234 ,4) | 1.234 | 1.234 | t
-- num_flex(123456 ,5) | 123456 | 123456 | t
-- num_flex(0 ,5) | 0 | 0 | t
-- num_flex(00000.00000 ,5) | 0 | 0 | t
-- num_flex(00000.00001 ,5) | 0.00001 | 0.00001 | t
-- num_flex(12345678901 ,5) | 12345678901 | 12345678901 | t
-- num_flex(123456789.1 ,5) | 123456789 | 123456789 | t
-- num_flex(1.234 ,-4) | 1.234 | 1.234 | t
--(11 rows)
您可以在 column definition 中声明 numeric
数据类型的 precision
(总位数)。只有小数点后的数字会四舍五入。如果小数点前的数字太多,你会得到一个错误。
缺点是 numeric(n)
实际上是 numeric(n,0)
,这是由 SQL 标准规定的。因此,如果通过将列的位数限制为 5
,您希望同时拥有 12345.0
和 0.12345
,则无法配置 numeric
来同时保留两者。 numeric(5)
会将0.12345
四舍五入为0
,numeric(5,5)
会将小数点右边的所有数字都舍入,拒绝12345
.
create table test (numeric_column numeric(5));
insert into test values (12345.123);
table test;
-- numeric_column
------------------
-- 12345
--(1 row)
insert into test values (123456.123);
--ERROR: numeric field overflow
--DETAIL: A field with precision 5, scale 0 must round to an absolute value less than 10^5.
我正在寻找位数有限的数字数据类型 (小数点前后)
该函数只删除小数点后的数字。 (PG 版本 >= 13)
create function num_flex( v numeric, d int) returns numeric as
$$
select case when v=0 then 0
when v < 1 and v > -1 then trim_scale(round(v, d - 1 ) )
else trim_scale(round(v, d - 1 - least(log(abs(v))::int,d-1) ) ) end;
$$
language sql ;
用于测试:
select num_flex( 0, 6)
union all
select num_flex( 1.22000, 6)
union all
select num_flex( (-0.000000123456789*10^x)::numeric,6)
from generate_series(1,15,3) t(x)
union all
select num_flex( (0.0000123456789*10^x)::numeric,6)
from generate_series(1,15,3) t(x) ;
它运行, 但有人有更好的主意或发现错误(一种情况,未实现)?
下一步就是把这个集成到PG中,这样我就可以写了
select 12.123456789::num_flex6 ;
select 12.123456789::num_flex7 ;
对于具有 6 位或 7 位数字的 num_flex 数据类型。 类型从 num_flex2 到 num_flex9。这可能吗?
你的函数有一些问题:
- 接受负数计数(参数
d
)。num_flex(1234,-2)
returns1200
- 您指定您希望该函数仅删除小数点后的数字,因此1234
是预期的。 - -1 和 1 之间的结果不正确。
num_flex(0.123,3)
returns0.12
而不是0.123
。我想如果你确实想在小数点左边计算 0,这也可能是你想要的效果。通常,当考虑数字的精度和小数位数时,0
会被忽略。 - 由于
::int
四舍五入的工作原理,您对小数点左侧数字的计数不正确。log(abs(11))::int
是1
但log(abs(51))::int
是2
。ceil(log(abs(v)))::int
returns2
在这两种情况下,同时保持int
类型仍然作为round()
. 中的第二个参数
create or replace function num_flex(
input_number numeric,
digit_count int,
is_counting_unit_zero boolean default false)
returns numeric as
$$
select trim_scale(
case
when input_number=0
then 0
when digit_count<=0 --avoids negative rounding
then round(input_number,0)
when (input_number between -1 and 1) and is_counting_unit_zero
then round(input_number,digit_count-1)
when (input_number between -1 and 1)
then round(input_number,digit_count)
else
round( input_number,
greatest( --avoids negative rounding
digit_count - (ceil(log(abs(input_number))))::int,
0)
)
end
);
$$
language sql;
这是一个测试
select *,"result"="should_be"::numeric as "is_correct" from
(values
('num_flex(0.1234 ,4)',num_flex(0.1234 ,4), '0.1234'),
('num_flex(1.234 ,4)',num_flex(1.234 ,4), '1.234'),
('num_flex(1.2340000 ,4)',num_flex(1.2340000 ,4), '1.234'),
('num_flex(0001.234 ,4)',num_flex(0001.234 ,4), '1.234'),
('num_flex(123456 ,5)',num_flex(123456 ,5), '123456'),
('num_flex(0 ,5)',num_flex(0 ,5), '0'),
('num_flex(00000.00000 ,5)',num_flex(00000.00000 ,5), '0'),
('num_flex(00000.00001 ,5)',num_flex(00000.00001 ,5), '0.00001'),
('num_flex(12345678901 ,5)',num_flex(12345678901 ,5), '12345678901'),
('num_flex(123456789.1 ,5)',num_flex(123456789.1 ,5), '123456789'),
('num_flex(1.234 ,-4)',num_flex(1.234 ,4), '1.234')
) as t ("operation","result","should_be");
-- operation | result | should_be | is_correct
----------------------------+-------------+-------------+------------
-- num_flex(0.1234 ,4) | 0.1234 | 0.1234 | t
-- num_flex(1.234 ,4) | 1.234 | 1.234 | t
-- num_flex(1.2340000 ,4) | 1.234 | 1.234 | t
-- num_flex(0001.234 ,4) | 1.234 | 1.234 | t
-- num_flex(123456 ,5) | 123456 | 123456 | t
-- num_flex(0 ,5) | 0 | 0 | t
-- num_flex(00000.00000 ,5) | 0 | 0 | t
-- num_flex(00000.00001 ,5) | 0.00001 | 0.00001 | t
-- num_flex(12345678901 ,5) | 12345678901 | 12345678901 | t
-- num_flex(123456789.1 ,5) | 123456789 | 123456789 | t
-- num_flex(1.234 ,-4) | 1.234 | 1.234 | t
--(11 rows)
您可以在 column definition 中声明 numeric
数据类型的 precision
(总位数)。只有小数点后的数字会四舍五入。如果小数点前的数字太多,你会得到一个错误。
缺点是 numeric(n)
实际上是 numeric(n,0)
,这是由 SQL 标准规定的。因此,如果通过将列的位数限制为 5
,您希望同时拥有 12345.0
和 0.12345
,则无法配置 numeric
来同时保留两者。 numeric(5)
会将0.12345
四舍五入为0
,numeric(5,5)
会将小数点右边的所有数字都舍入,拒绝12345
.
create table test (numeric_column numeric(5));
insert into test values (12345.123);
table test;
-- numeric_column
------------------
-- 12345
--(1 row)
insert into test values (123456.123);
--ERROR: numeric field overflow
--DETAIL: A field with precision 5, scale 0 must round to an absolute value less than 10^5.