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。这可能吗?

你的函数有一些问题:

  1. 接受负数计数(参数d)。 num_flex(1234,-2) returns 1200 - 您指定您希望该函数仅删除小数点后的数字,因此 1234 是预期的。
  2. -1 和 1 之间的结果不正确。num_flex(0.123,3) returns 0.12 而不是 0.123。我想如果你确实想在小数点左边计算 0,这也可能是你想要的效果。通常,当考虑数字的精度和小数位数时,0 会被忽略。
  3. 由于 ::int 四舍五入的工作原理,您对小数点左侧数字的计数不正确。 log(abs(11))::int1log(abs(51))::int2ceil(log(abs(v)))::int returns 2 在这两种情况下,同时保持 int 类型仍然作为 round().
  4. 中的第二个参数
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.00.12345,则无法配置 numeric 来同时保留两者。 numeric(5)会将0.12345四舍五入为0numeric(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.