2013-01-13 244 views
8

有沒有人找到Oracle的PERCENTILE_CONT函數的PostgreSQL等價物?我搜查了,找不到一個,所以我寫了我自己的。相當於Oracle的PERCENTILE_CONT函數的PostgreSQL

這裏是我希望能幫助你的解決方案。

我工作的公司希望將Java EE Web應用程序從使用Oracle數據庫遷移到使用PostgreSQL。幾個存儲過程嚴重依賴於Oracle獨特的PERCENTILE_CONT()函數。這個函數在PostgreSQL中不存在。

我試着搜索一下,看看是否有人將這個函數「移植」到PG中無濟於事。

回答

17

更多的搜索後,我發現,上市甲骨文如何在實現此功能的僞代碼的頁面:

http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions110.htm

我決心內PG寫我自己的函數來模仿Oracle的功能。

我發現的陣列在::

http://postgres.cz/wiki/PostgreSQL_SQL_Tricks#General_array_sort

Sorting array elements

這裏(爲了清楚)是David的代碼由David腳鏈排序技術:

CREATE OR REPLACE FUNCTION array_sort (ANYARRAY) 
RETURNS ANYARRAY LANGUAGE SQL 
AS $$ 
SELECT ARRAY(
    SELECT $1[s.i] AS "foo" 
    FROM 
     generate_series(array_lower($1,1), array_upper($1,1)) AS s(i) 
    ORDER BY foo 
); 
$$; 

所以這裏是我記得的功能E:

CREATE OR REPLACE FUNCTION percentile_cont(myarray real[], percentile real) 
RETURNS real AS 
$$ 

DECLARE 
    ary_cnt INTEGER; 
    row_num real; 
    crn real; 
    frn real; 
    calc_result real; 
    new_array real[]; 
BEGIN 
    ary_cnt = array_length(myarray,1); 
    row_num = 1 + (percentile * (ary_cnt - 1)); 
    new_array = array_sort(myarray); 

    crn = ceiling(row_num); 
    frn = floor(row_num); 

    if crn = frn and frn = row_num then 
    calc_result = new_array[row_num]; 
    else 
    calc_result = (crn - row_num) * new_array[frn] 
      + (row_num - frn) * new_array[crn]; 
    end if; 

    RETURN calc_result; 
END; 
$$ 
    LANGUAGE 'plpgsql' IMMUTABLE; 

下面是一些比較測試的結果:

CREATE TABLE testdata 
(
    intcolumn bigint, 
    fltcolumn real 
); 

下面是測試數據:

insert into testdata(intcolumn, fltcolumn) values (5, 5.1345); 
insert into testdata(intcolumn, fltcolumn) values (195, 195.1345); 
insert into testdata(intcolumn, fltcolumn) values (1095, 1095.1345); 
insert into testdata(intcolumn, fltcolumn) values (5995, 5995.1345); 
insert into testdata(intcolumn, fltcolumn) values (15, 15.1345); 
insert into testdata(intcolumn, fltcolumn) values (25, 25.1345); 
insert into testdata(intcolumn, fltcolumn) values (495, 495.1345); 
insert into testdata(intcolumn, fltcolumn) values (35, 35.1345); 
insert into testdata(intcolumn, fltcolumn) values (695, 695.1345); 
insert into testdata(intcolumn, fltcolumn) values (595, 595.1345); 
insert into testdata(intcolumn, fltcolumn) values (35, 35.1345); 
insert into testdata(intcolumn, fltcolumn) values (30195, 30195.1345); 
insert into testdata(intcolumn, fltcolumn) values (165, 165.1345); 
insert into testdata(intcolumn, fltcolumn) values (65, 65.1345); 
insert into testdata(intcolumn, fltcolumn) values (955, 955.1345); 
insert into testdata(intcolumn, fltcolumn) values (135, 135.1345); 
insert into testdata(intcolumn, fltcolumn) values (19195, 19195.1345); 
insert into testdata(intcolumn, fltcolumn) values (145, 145.1345); 
insert into testdata(intcolumn, fltcolumn) values (85, 85.1345); 
insert into testdata(intcolumn, fltcolumn) values (455, 455.1345); 

下面是比較結果:

ORACLE RESULTS 
ORACLE RESULTS 

select percentile_cont(.25) within group (order by fltcolumn asc) myresult 
from testdata; 
select percentile_cont(.75) within group (order by fltcolumn asc) myresult 
from testdata; 

myresult 
- - - - - - - - 
57.6345     

myresult 
- - - - - - - - 
760.1345    

POSTGRESQL RESULTS 
POSTGRESQL RESULTS 

select percentile_cont(array_agg(fltcolumn), 0.25) as myresult 
from testdata; 

select percentile_cont(array_agg(fltcolumn), 0.75) as myresult 
from testdata; 

myresult 
real 
57.6345 

myresult 
real 
760.135 

我希望這可以幫助某人o通過不必重新發明輪子。

享受! Ray Harris

+0

可以通過使用ORDER BY避免arroy_sort。它實際上比array_sort快一點。 – echo

+0

我用這個函數並更新了我的數據庫。從9.4開始有一個名稱相同的函數,由於命名相似性和實現方式,這兩個函數之間存在衝突。這發生在我從9.3遷移到9.6之後。 –

2

使用PostgreSQL 9。4存在用於百分原生支持現在,在有序集聚集函數實施:

percentile_cont(fraction) WITHIN GROUP (ORDER BY sort_expression) 

連續百分位數:返回對應於排序指定 分數的值,相鄰的輸入項 之間進行內插如果需要的話

percentile_cont(fractions) WITHIN GROUP (ORDER BY sort_expression) 

多個連續百分位數:返回匹配 的級分參數的形狀的結果的陣列,無線個通過對應於百分

的值替換每個非空元件 更多細節參見文檔:http://www.postgresql.org/docs/current/static/functions-aggregate.html