6

PostgreSQLのround(numeric、integer)関数は、切り上げのみを行います。

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

41.025を返すラウンド関数が必要であり、(非常に驚くべきことに)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;

数値をキャストして正規表現を使用する必要があります...それが(おそらく)パフォーマンスを殺すものです。

ご存知のとおり、これが必要なのは、2つの異なる列(2つの異なるテーブル)に格納されているが、数値データ型が異なる数値を比較する必要があるためです。1つはdoubleで、もう1つはrealです。問題は、実際のデータ型の列に挿入するときに、PostgreSQLが数学関数を介してそのようなオプションを提供しないのに、ROUNDHALFDOWNを実行することです。

編集:
関数は実際にバグがあります。動作中の関数のパフォーマンスを改善する試みとしての最初の迅速な書き直しでしたが、非常に低速でした。

動作は次のように一致する必要があります。
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のラウンド関数は次のようになります。

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

6 に答える 6

6

以下のように半分に切り捨てることができる新しい関数を作成しなくても、メソッドは非常に高速です。

-半分を切り上げます

round($n, 3)

-半分を切り下げます

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

2つの異なる列(2つの異なるテーブル)に格納されているが、数値データ型が異なる数値を比較する必要があります。1つはdoubleで、もう1つはrealです。

これは非常に高速でシンプルなはずです。

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 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

$$;

変更してジェネリックを使用できます(小数点以下3桁の場合は0.001、小数点以下4桁の場合は0.0001など)

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 に答える
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 に答える