Написание пользовательской агрегатной функции

В последнее время я практикуюсь и изучаю 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.

Можете ли вы описать проблему/ошибку, с которой вы столкнулись?

Laurenz Albe 08.04.2019 16:43

Я запустил ваш код, он работает, в чем ваша проблема?

Rémy Baron 08.04.2019 17:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В принципе вы все делаете правильно, но с вашей реализацией все еще есть некоторые проблемы:

  1. Ваш агрегат выдаст ошибку, если таблица пуста. Скорее, он должен вернуться NULL.

  2. Если первое значение, которое вы агрегируете, равно NULL, то ваша агрегация будет работать неправильно. если

    minVal := $1[1];
    

    устанавливает minVal на NULL, тогда все будущие сравнения minVal >= i будут нет равными TRUE, а конечным результатом будет NULL, а это не то, что вам нужно.

  3. Вы собираете все значения в массив, который станет довольно большим, если вы агрегируете много строк. Во-первых, это подвергает вас опасности нехватки памяти с массивом, а затем производительность агрегата будет не такой хорошей, как могла бы быть.

    Гораздо лучше выполнять сравнение в SFUNC: начать с INITCOND = NULL, использовать STYPE = numeric и выполнять агрегацию всякий раз, когда вы обрабатываете новое значение. Соответствующая часть SFUNC будет выглядеть так:

    IF $1 IS NULL OR $1 > $2
        RETURN $2;
    ELSE
        RETURN $1;
    END IF;
    

    Таким образом, вам вообще не нужен FINALFUNC.

Да, я знаю, что если мой массив станет большим, моя память может закончиться. Но в написанном вами коде вы передаете два параметра: 1 доллар и 2 доллара. Что такое $2?

SHl MRJ 08.04.2019 18:11

Я написал части возможной «функции перехода состояния» (SFUNC в CREATE AGGREGATE). См. документацию.

Laurenz Albe 08.04.2019 18:28

Другие вопросы по теме