Подготовить
Ваши формулы выглядят так:
d*b+(l*4+r)+(i/d)+s
Я бы заменил переменные на $n
нотации, чтобы они могли быть заменены значениями непосредственно в plpgsql EXECUTE
(см. Ниже):
$1*$5+($3*4+$2)+($6/$1)+$4
Вы можете дополнительно сохранить исходные формулы (для человеческого глаза) или динамически сгенерировать эту форму с помощью следующего выражения:
SELECT regexp_replace(regexp_replace(regexp_replace(
regexp_replace(regexp_replace(regexp_replace(
'd*b+(l*4+r)+(i/d)+s'
, '\md\M', '$1', 'g')
, '\mr\M', '$2', 'g')
, '\ml\M', '$3', 'g')
, '\ms\M', '$4', 'g')
, '\mb\M', '$5', 'g')
, '\mi\M', '$6', 'g');
Просто убедитесь, что ваш перевод звучит правильно. Некоторое объяснение выражений регулярных выражений :
\ m .. соответствует только в начале слова
\ M .. соответствует только в конце слова
4-й параметр 'g'
.. заменить глобально
Основная функция
CREATE OR REPLACE FUNCTION f_calc(
d int -- days worked that month
,r int -- new nodes accuired
,l int -- loyalty score
,s numeric -- subagent commission
,b numeric -- base rate
,i numeric -- revenue gained
,formula text
,OUT result numeric
) RETURNS numeric AS
$func$
BEGIN
EXECUTE 'SELECT '|| formula
INTO result
USING $1, $2, $3, $4, $5, $6;
END
$func$ LANGUAGE plpgsql SECURITY DEFINER IMMUTABLE;
Вызов:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Возвращает:
29.6000000000000000
Основные моменты
Функция принимает 6 значений параметра и formula text
как 7-й. Я ставлю формулу последней, чтобы мы могли использовать $1 .. $6
вместо $2 .. $7
. Просто для удобства чтения.
Я назначил типы данных для значений по своему усмотрению. Назначьте подходящие типы (для реализации базовых проверок работоспособности) или просто сделайте их все numeric
:
Передайте значения для динамического выполнения с USING
предложением. Это позволяет избежать бросания вперед и назад и делает все проще, безопаснее и быстрее.
Я использую OUT
параметр, потому что это более элегантно и делает для более короткого более ясного синтаксиса. Окончательный RETURN
вариант не требуется, значение параметра (ов) OUT возвращается автоматически.
Рассмотрите лекцию по безопасности @Chris и главу «Безопасное написание функций БЕЗОПАСНОСТИ» в руководстве. В моем дизайне единственной точкой внедрения является сама формула.
Вы можете использовать значения по умолчанию для некоторых параметров, чтобы еще больше упростить вызов.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) илиALTER FUNCTION foo(text) SECURITY DEFINER
a+b
хранится в столбце текстового типа в таблице, тогда у меня есть функция,foo(a int, b int,formula text)
если она получает формулу a + b, как я могу заставить функцию фактически делать a + b вместо мне нужно иметь очень длинный регистр для всех возможных формул и повторять код во всех сегментах?Альтернативой простому сохранению формулы и последующему ее выполнению (которая, как упоминал Крис, есть проблемы с безопасностью ) будет иметь отдельную таблицу с именем,
formula_steps
которая в основном будет содержать переменные и операторы, а также последовательность, в которой они выполняются. Это будет немного больше работы, но будет более безопасным. Таблица может выглядеть так:Другой вариант - использовать стороннюю библиотеку / инструмент для оценки математических выражений. Это сделает вашу базу данных менее уязвимой для внедрения SQL-кода, но теперь вы просто перенесли возможные проблемы с безопасностью на ваш внешний инструмент (который все еще может быть довольно безопасным).
Последний вариант - написать (или загрузить) процедуру, которая оценивает математические выражения. Существуют известные алгоритмы для этой проблемы, поэтому не составит труда найти информацию в Интернете.
источник