PostgreSQL-эквивалент функции Oracle PERCENTILE_CONT
Кто-нибудь нашел в PostgreSQL эквивалент функции Oracle PERCENTILE_CONT? Я искал и не мог найти один, поэтому я написал свой.
Вот решение, которое, я надеюсь, поможет вам.
Компания, в которой я работаю, хотела перевести веб-приложение Java EE с базы данных Oracle на PostgreSQL. Несколько хранимых процедур в значительной степени основаны на использовании уникальной функции Oracle PERCENTILE_CONT(). Эта функция не существует в PostgreSQL.
Я попытался найти, чтобы определить, перенес ли кто-нибудь эту функцию в PG, но безрезультатно.
2 ответа
После дополнительных поисков я нашел страницу, на которой перечислен псевдокод того, как Oracle реализует эту функцию:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions110.htm
Я решил написать свою собственную функцию в PG, чтобы имитировать функцию Oracle.
Я нашел технику сортировки массивов Дэвида Феттера в:
http://postgres.cz/wiki/PostgreSQL_SQL_Tricks
а также
Вот (для ясности) код Дэвида:
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
);
$$;
Итак, вот функция, которую я написал:
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
Я надеюсь, что это поможет кому-то, не изобретая велосипед.
Наслаждайтесь! Рэй Харрис
В 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