Przygotować
Twoje formuły wyglądają tak:
d*b+(l*4+r)+(i/d)+s
$n
Zastąpiłbym zmienne notacją, aby można je było zastąpić wartościami bezpośrednio w plpgsql EXECUTE
(patrz poniżej):
$1*$5+($3*4+$2)+($6/$1)+$4
Możesz dodatkowo przechowywać oryginalne formuły (dla ludzkiego oka) lub dynamicznie generować ten formularz z wyrażeniem:
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');
Tylko upewnij się, że tłumaczenie jest prawidłowe. Kilka wyjaśnień wyrażeń regularnych :
\ m .. dopasowuje tylko na początku słowa
\ M .. dopasowuje tylko na końcu słowa
Czwarty parametr 'g'
... wymienić globalnie
Funkcja podstawowa
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;
Połączenie:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Zwroty:
29.6000000000000000
Najważniejsze punkty
Funkcja przyjmuje parametr 6 wartości i zajmuje 7 formula text
miejsce. Stawiam formułę na końcu, abyśmy mogli użyć $1 .. $6
zamiast $2 .. $7
. Tylko ze względu na czytelność.
Przypisałem typy danych do wartości, które uznałem za stosowne. Przypisz odpowiednie typy (w celu wprowadzenia podstawowych kontroli poczytalności) lub po prostu wykonaj je wszystkie numeric
:
Przekaż wartości do dynamicznego wykonania z USING
klauzulą. Pozwala to uniknąć rzucania w tę iz powrotem, a wszystko jest prostsze, bezpieczniejsze i szybsze.
Używam OUT
parametru, ponieważ jest on bardziej elegancki i zapewnia krótszą, bardziej przejrzystą składnię. Ostateczny RETURN
nie jest potrzebny, wartości parametrów OUT są zwracane automatycznie.
Rozważ wykład @Chris na temat bezpieczeństwa i rozdział „Pisanie DEFINICERA BEZPIECZEŃSTWA w bezpieczny sposób” w podręczniku. W moim projekcie pojedynczym punktem wstrzyknięcia jest sama formuła.
Możesz użyć wartości domyślnych dla niektórych parametrów w celu dalszego uproszczenia połączenia.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) lub możeszALTER FUNCTION foo(text) SECURITY DEFINER
a+b
Jest przechowywany w kolumnie typu tekstowego w tabeli, a następnie mam funkcję,foo(a int, b int,formula text)
jeśli otrzyma formułę a + b, jak mogę sprawić, by funkcja faktycznie wykonywała a + b zamiast muszę mieć bardzo długą instrukcję dla wszystkich możliwych formuł i powtarzać kod we wszystkich segmentach?Alternatywą po prostu zapisanie formuły, a następnie jej wykonanie (co, jak wspomniał Chris, ma problemy z bezpieczeństwem ), byłoby utworzenie oddzielnej tabeli,
formula_steps
która w zasadzie zawierałaby zmienne i operatory oraz kolejność ich wykonywania. Byłoby to trochę więcej pracy, ale byłoby bardziej bezpieczne. Tabela może wyglądać następująco:Inną opcją byłoby użycie biblioteki / narzędzia innej firmy do oceny wyrażeń matematycznych. To sprawiłoby, że twoja baza danych byłaby mniej podatna na wstrzykiwanie SQL, ale teraz przeniosłeś możliwe problemy bezpieczeństwa na swoje narzędzie zewnętrzne (co nadal może być całkiem bezpieczne).
Ostatnią opcją byłoby napisanie (lub pobranie) procedury oceniającej wyrażenia matematyczne. Istnieją znane algorytmy tego problemu, więc znalezienie informacji w Internecie nie powinno być trudne.
źródło