В последнее время я практикуюсь и изучаю PL/pgSQL. Я застрял на создании пользовательской агрегатной функции.
Код ниже работает отлично, но я не могу написать агрегатную функцию такого типа:
SELECT my_aggregate_function(column) from table;
Вот код "частично работающего пользовательского агрегата":
CREATE OR REPLACE FUNCTION searchMinValue (numeric[]) RETURNS numeric AS $$
DECLARE
i numeric;
minVal numeric;
BEGIN
minVal := $1[1];
IF ARRAY_LENGTH($1,1) > 0 THEN --Checking whether the array is empty or not
<<confrontoMinimo>>
FOREACH i IN ARRAY $1 LOOP --Looping through the entire array, passed as parameter
IF minVal >= i THEN
minVal := i;
END IF;
END LOOP confrontoMinimo;
ELSE
RAISE NOTICE 'Invalid parameter % passed to the aggregate function',$1;
--Raising exception if the parameter passed as argument points to null.
RAISE EXCEPTION 'Cannot find Max value. Parameter % is null', $1
USING HINT = 'You cannot pass a null array! Check the passed parameter';
END IF;
RETURN minVal;
END;
$$ LANGUAGE plpgsql;
CREATE AGGREGATE searchMinValueArray (numeric)
(
sfunc = array_append,
stype = numeric[],
finalfunc = searchMinValue,
initCond = '{}'
);
with w(v) as (select 5 union all select 2 union all select 3)
select min(v) "Normal Aggregate", searchMinValueArray(v) "My Customed Aggregate" from w;
Как я уже говорил, я хотел бы вызвать свою пользовательскую агрегатную функцию следующим образом:
SELECT my_aggregate_function(column) from table;
где таблица Customers
, а столбец salary
типа numeric
.
Я запустил ваш код, он работает, в чем ваша проблема?
В принципе вы все делаете правильно, но с вашей реализацией все еще есть некоторые проблемы:
Ваш агрегат выдаст ошибку, если таблица пуста. Скорее, он должен вернуться NULL
.
Если первое значение, которое вы агрегируете, равно NULL
, то ваша агрегация будет работать неправильно. если
minVal := $1[1];
устанавливает minVal
на NULL
, тогда все будущие сравнения minVal >= i
будут нет равными TRUE
, а конечным результатом будет NULL
, а это не то, что вам нужно.
Вы собираете все значения в массив, который станет довольно большим, если вы агрегируете много строк. Во-первых, это подвергает вас опасности нехватки памяти с массивом, а затем производительность агрегата будет не такой хорошей, как могла бы быть.
Гораздо лучше выполнять сравнение в SFUNC
: начать с INITCOND = NULL
, использовать STYPE = numeric
и выполнять агрегацию всякий раз, когда вы обрабатываете новое значение. Соответствующая часть SFUNC
будет выглядеть так:
IF $1 IS NULL OR $1 > $2
RETURN $2;
ELSE
RETURN $1;
END IF;
Таким образом, вам вообще не нужен FINALFUNC
.
Да, я знаю, что если мой массив станет большим, моя память может закончиться. Но в написанном вами коде вы передаете два параметра: 1 доллар и 2 доллара. Что такое $2?
Я написал части возможной «функции перехода состояния» (SFUNC
в CREATE AGGREGATE
). См. документацию.
Можете ли вы описать проблему/ошибку, с которой вы столкнулись?