3

的一輪的PostgreSQL(數字,整數)函數只向上舍:PostgreSQL的圓半年下來功能

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; 

我需要轉換的數值,並使用正則表達式......這就是(我想)殺死表演。

就這樣您知道:我們需要這樣做是因爲我們必須比較存儲在兩個不同列(兩個不同表)中但具有不同數值數據類型的數字:其中一個是雙精度,另一個是實數。問題是,當插入一個真正的數據類型列時,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的輪函數給出:

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

你會想'-41.0255'四捨五入到'-41.025'或' -41.026'?另外:你想'41.02551'四捨五入到'41.025'或'41.026'? –

+0

您好@ erwin-brandstetter,該功能實際上是竊聽。第一次改寫是爲了提高工作函數的性能,但速度很慢。行爲必須匹配以下內容:如果舍入舍去的十進制數字爲<=5 => trunc ELSE舍入。例如:select round_half_down(cast(41.002555 as numeric),4) - 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 – BangTheBank

+0

我更新了我的答案以匹配您的更新。順便說一下,這種信息應該進入你的*問題*,而不是寫入難以閱讀的評論。 –

回答

2

我們比較(上兩個不同的表),其分別存放在兩個不同的列 號但是使用不同的數字數據類型:一個是 雙精度,另一個是實數。

這應該是非常快速和簡單的:

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

基本上只投了double precisionreal進行比較。


如果不爲你工作,這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表達式。

% .. modulo operator
^ .. exponentiation


這裏是一個快速測試,你可以使用基準測試:

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) 

它還演示瞭如何@帕爾文的功能,你編輯的版本誤判 - 他們trunc(),他們不應該。

+0

@ user500501:那麼爲你或者功能鑄造「真實」作品? –

+0

非常感謝您的回答。奇怪的是,鑄造不能很好地工作。我正在做更多的測試以找出原因,因爲它聽起來是對我的問題的正確答案。奇怪的是,它不匹配所有的行,它需要很長的時間才能找到不匹配的行來發現問題。基本上,REAL列中的所有值必須與DOUBLE PRECISION列中的「cast to REAL」值相匹配(因爲這只是將值插入REAL列時發生的情況),但事實並非如此!4267717中的73761行未被匹配。 – BangTheBank

+1

不適用於負值。 '''CASE當$ 1%0.1^$ 2'''應該是'''ABS時的情況($ 1%0.1^$ 2)''' – sufleR

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 EDITED使用通用: @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 

$$; 
+0

您的功能都會錯誤計算。我在我的答案中添加了一個演示。 –

+0

@Erwin:其實第二個是我提出的Parveen函數的一個廣義版本:) – BangTheBank

+0

@BangTheBank:如果你有一些實質性的不同(比如這裏),最好自己添加一個答案。所以不會阻止回答你自己的問題。 –

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; 

試試這個希望能幫助ü

0

這裏有一個更簡單的方法

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'; 
1

的方法,而無需創建一個新的功能很快可以圓半年下來,如下:

- 半輪起來

round($n, 3) 

- 回合半年下來

round($n-0.5, 3) 
1

其簡單:

嘗試使用此功能

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$