6

PostgreSQL 中的 round(numeric,integer) 函数只四舍五入:

round(cast (41.0255 as numeric),3) ==> 41.026

因为我们需要一个返回 41.025 的 round 函数并且(非常令人惊讶)在 PostgreSQL 中没有这样的函数(我们使用的是 9.1.5),所以我们编写了一个“包装器”函数,它在第一个版本中非常幼稚并且粗糙...但是由于在 plpgsql 中缺乏对此类问题的本机支持,我们没有找到更好的方法。

代码如下所示。问题是它对于我们的目的来说太慢了。你能建议一个更好的方法来处理这个任务吗?

这是代码:

    CREATE OR REPLACE FUNCTION round_half_down(numeric,integer) RETURNS numeric 
    AS $$
    DECLARE
      arg ALIAS FOR $1;
      rnd ALIAS FOR $2;
      tmp1 numeric;
      res numeric;
    BEGIN
      tmp1:=arg;
      IF cast(tmp1 as varchar) ~ '5$'  THEN res:=trunc(arg,rnd);
      ELSE res:=round(arg,rnd);
      END IF;

      RETURN res;
    END;
    $$ LANGUAGE plpgsql;

我需要转换数值并使用正则表达式......这就是(我想)扼杀表演的原因。

你知道:我们需要这个,因为我们必须比较存储在两个不同列(在两个不同表上)但具有不同数字数据类型的数字:一个是双精度数,一个是实数。问题是当插入一个真实的数据类型列时,PostgreSQL 执行 ROUND HALF DOWN 而它没有通过它的数学函数提供这样的选项!

编辑:
该功能实际上是错误的。是第一次快速重写,试图提高工作功能的性能,但速度很慢。

行为必须符合以下条件:
IF四舍五入中推迟的小数位被四舍五入<=5 => trunc
ELSE

一些例子:

select round_half_down(cast (41.002555 as numeric),3) -- 41.002 
select round_half_down(cast (41.002555 as numeric),4) -- 41.0025 
select round_half_down(cast (41.002555 as numeric),5) -- 41.00255 

而 PostgreSQL 中的 round 函数给出:

select round(cast (41.002555 as numeric),3) -- 41.003
4

6 回答 6

6

一种方法非常快,无需创建新的 FUNCTION,它可以向下舍入一半,如下所示:

-- 上半场

round($n, 3)

-- 下半场

round($n-0.5, 3)
于 2014-10-21T11:48:41.497 回答
3

我们必须比较存储在两个不同列(在两个不同表上)但具有不同数字数据类型的数字:一个是双精度数,一个是实数。

这应该非常快速和简单:

SELECT dp_col, real_col
FROM   tbl
WHERE  dp_col::real = real_col

基本上,只需将double precision数字转换real为进行比较。


如果这对您不起作用,则此SQL 函数应该可以完成适当的工作并更快地工作:

CREATE OR REPLACE FUNCTION round_half_down1(numeric, int)
  RETURNS numeric LANGUAGE sql AS
$func$
SELECT CASE WHEN abs($1%0.1^$2) < .6 * 0.1^$2 THEN
         trunc($1, $2)
    ELSE round($1, $2) END;
$func$

现在修复了负数,并在评论中输入了来自@sufleR 的信息。
您也可以只使用包含的CASE表达式。

% .. 模运算符
^ .. 取幂


这是一个可用于基准测试的快速测试:

SELECT n                                   -- Total runtime: 36.524 ms
      ,round_half_down1(n,3)               -- Total runtime: 70.493 ms
      ,round_down_to_decimal_places(n,3)   -- Total runtime: 74.690 ms
      ,round_half_down(n,3)                -- Total runtime: 82.191 ms
FROM  (SELECT random()::numeric AS n FROM generate_series(1,10000)) x
WHERE  round_down_to_decimal_places(n,3)
    <> round_half_down1(n,3)

它还演示了@Parveen 的函数和您编辑的版本是如何计算错误的——它们trunc()不应该出现在错误的地方。

于 2012-09-21T09:09:40.527 回答
2

这很简单:

尝试使用此功能

CREATE OR REPLACE FUNCTION ROUND_HALF_DOWN(NUMERIC)
  RETURNS NUMERIC LANGUAGE SQL AS
$FUNC$
  SELECT CASE WHEN ($1%1) < 0.6 THEN FLOOR($1) ELSE CEIL($1) END;
$FUNC$
于 2015-07-11T02:39:42.123 回答
1

这是一个更简单的方法

CREATE OR REPLACE FUNCTION roundHalfDown(value NUMERIC, prec INTEGER)
RETURNS NUMERIC AS $$
BEGIN
  RETURN trunc(value * 10^prec + 0.5 - 0.000000001) / 10^prec;
END
$$ LANGUAGE 'plpgsql';
于 2014-02-21T07:02:17.897 回答
0
CREATE OR REPLACE FUNCTION public.round_half_down (numeric,integer)
RETURNS numeric AS
$body$
DECLARE
  arg ALIAS FOR $1;
  rnd ALIAS FOR $2;
  tmp1 numeric;
  res numeric;
  vra varchar;
  inta integer;
  ifa boolean;
 BEGIN
    tmp1:= arg;
    vra := substr(cast((arg - floor(arg)) as varchar),3);
    ifa := null;
    FOR i IN 1 .. length(vra) LOOP
        inta := CAST((substr(vra,i,1)) as integer);
        IF (i > rnd) THEN
            IF ((ifa is null) AND inta >= 6)THEN
                ifa := true;
            ELSE
                ifa := false;
            END IF;
        END IF;
    END LOOP;

    IF ifa THEN 
        res:=trunc(arg,rnd);
    ELSE 
        res:=round(arg,rnd);
    END IF;
    RETURN res;
END;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

试试这个希望可以帮助你

于 2013-09-27T07:54:52.390 回答
0
create or replace function round_down_to_decimal_places(numeric,integer)
returns numeric stable language sql as $$ 

select
case
when $1 >= 0 then
case when $1 - round($1, 3) < 0 then round($1, 3) - 0.001 else 
round($1, 3) end
else
case when $1 - round($1, 3) > 0 then round($1, 3) + 0.001 else 
round($1, 3) end
end

$$;

您可以通过更改来使用通用(0.001 表示 3 位小数,0.0001 表示 4 位小数等,)

由 OP 编辑​​:@Parveel:我已经修改了您的函数,以使其以一般方式工作。

create or replace function round_half_down(numeric,integer)
returns numeric stable language sql as $$ 

select
case
when $1 >= 0 then
    case 
        when ($1 - round($1, $2)) < 0 then cast((round($1, $2) - (1.0/(10^$2))) as numeric) 
    else round($1, $2) end
else
    case 
        when ($1 - round($1, $2)) > 0 then cast((round($1, $2) + (1.0/(10^$2))) as numeric)
    else round($1, $2) end
end

$$;
于 2012-09-21T09:11:17.730 回答