Как вызвать хранимую функцию, имя которой возвращается другой хранимой функцией в Oracle plsql?

Я попытался сделать управление версиями для расчета заработной платы в нашем приложении для расчета заработной платы. Я создал хранимую функцию для каждой версии формулы в соответствии с законодательством, которое применяется в любой момент времени, а затем я сохранил имена этих функций в таблице вместе с датой начала действия каждой функции. Затем я сделал функцию, которая извлекает имя (и возвращает это имя как varchar) функции, применимой к формуле в данный момент времени. В представлении, которое выполняет все вычисления, я попытался вызвать эту функцию, чтобы получить имя функции и использовать это возвращенное имя для вызова функции формулы. Все это сделано в инструкции select. Я пытаюсь вызвать функцию, имя которой возвращается другой функцией, например: (function1(param1, param2,...))(paramx, paramy,...), но это не работает. Есть ли способ использовать имя, возвращаемое функцией1, для вызова функции с этим именем и вводом (параметры paramx, paramy,...)?


person Voinea Gabriel    schedule 19.02.2016    source источник
comment
используйте немедленное выполнение в plsql. Со строкой примерно так. начать процедуру(); конец;. Если вы вызываете это внутри функции и если функция вызывается из выбора, убедитесь, что есть мутация.   -  person Shriram M.    schedule 19.02.2016
comment
Все ли ваши функции имеют одинаковое количество и типы аргументов и один и тот же тип возвращаемого значения?   -  person Alex Poole    schedule 19.02.2016


Ответы (2)


Функции не являются объектами первого класса, которые могут быть возвращены из других функций, и имя функции, содержащееся в переменной, не является фактическим кодом, который может быть выполнен. Однако вы, вероятно, можете сделать то, что пытаетесь сделать, используя динамический SQL:

DECLARE
  param1         NUMBER;
  param2         VARCHAR2(2000);
  function_name  VARCHAR2(2000);
  paramx         NUMBER;
  paramy         VARCHAR2(2000);
  plsql_block    VARCHAR2(2000);
  result         NUMBER;  -- assumes the function returns a NUMBER
BEGIN
  param1 := 123;    -- or whatever is appropriate
  param2 := 'abc';  -- or whatever is appropriate

  function_name := function1(param1, param2);

  -- assume that function_name now contains 'some_function'

  paramx := 456;    -- or whatever is appropriate
  paramy := 'def';  -- or whatever is appropriate

  plsql_block:= 'BEGIN :r := ' || function_name || '(:px, :py); END;';

  -- plsql_block should now contain 'BEGIN :r := some_function(:px, :py); END;'

  EXECUTE IMMEDIATE plsql_block USING IN OUT result, paramx, paramy;

  DBMS_OUTPUT.PUT_LINE('result = ' || result);
END;

Удачи.

person Bob Jarvis - Reinstate Monica    schedule 19.02.2016
comment
Функции не являются объектами первого класса, а объектно-реляционные типы с функциями-членами. Теоретически вы можете создавать и хранить типы и использовать полиморфизм. Хотя на практике динамический SQL лучше в 99% случаев, даже если это не классный объектно-ориентированный способ сделать это. - person Jon Heller; 20.02.2016
comment
@JonHeller - Если быть честным, я должен признать, что я думаю, что вызов функций путем извлечения их имен из базы данных, а затем создание блока BEGIN...END для их динамического вызова звучит как кошмар обслуживания. То, что человек МОЖЕТ что-то сделать, не означает, что он ДОЛЖЕН... :-) - person Bob Jarvis - Reinstate Monica; 20.02.2016

Если все функции, которые вам, возможно, придется вызывать, известны и существуют на момент компиляции основной функции, вы можете использовать переменную, которую вам нужно решить, какую из них вызывать. В очень грубой схеме:

create function master_salary(p_date date)
return number as
  l_function_name all_objects.object_name%type;
begin
  l_function_name := choose_function(p_date);

  case l_function
    when 'function_a' then
      return function_a;
    when 'function_b' then
      return function_b(some_arg);
    when 'function_c' then
      return function_c(some_arg, another_arg);
    else
      raise_application_error(-20001, 'Unknown function ' || l_function_name);
  end case;
end;
/

Это позволяет избежать динамического SQL и позволяет вам иметь функции с разными номерами и/или типами аргументов, а также легче следить за тем, что происходит.

Если вы добавляете больше функций на лету, то вам, вероятно, не следует этого делать — по крайней мере, не за пределами какого-то механизма управления исходным кодом и выпуска, который позволит вам поддерживать основную функцию в шаге. Вы могли бы, в качестве запасного варианта, сделать так, чтобы случай по умолчанию пытался выполнить любое имя функции, которое у вас есть, динамически (как показывает Боб Джарвис), если это не то, что вы ожидаете; но вам понадобятся согласованные номера аргументов и типы данных, и это потенциально открывает уязвимость, если таблица, из которой вы получаете имя функции, может быть изменена.

person Alex Poole    schedule 19.02.2016
comment
Я попробую это решение, потому что я не знаком с динамическим sql, а таблица с именами функций будет поддерживаться нашей командой разработчиков и недоступна для других пользователей. Мы постараемся также поддерживать и документировать главную функцию вместе с таблицами и функциями формул. Большое спасибо. - person Voinea Gabriel; 20.02.2016