Bereiten
Ihre Formeln sehen folgendermaßen aus:
d*b+(l*4+r)+(i/d)+s
Ich würde die Variablen durch $n
Notation ersetzen, damit sie direkt in plpgsql durch Werte ersetzt werden können EXECUTE
(siehe unten):
$1*$5+($3*4+$2)+($6/$1)+$4
Sie können Ihre Originalformeln zusätzlich (für das menschliche Auge) speichern oder dieses Formular dynamisch mit einem Ausdruck wie dem folgenden generieren:
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');
Stellen Sie einfach sicher, dass Ihre Übersetzung solide ist. Einige Erklärungen für die regulären Ausdrücke :
\ m .. stimmt nur am Anfang eines Wortes
überein \ M .. stimmt nur am Ende eines Wortes überein
4. Parameter 'g'
.. global ersetzen
Hauptfunktion
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;
Anruf:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Kehrt zurück:
29.6000000000000000
Hauptpunkte
Die Funktion nimmt 6 Wert Parameter und formula text
als 7 .. Ich habe die Formel zuletzt gesetzt, damit wir $1 .. $6
stattdessen verwenden können $2 .. $7
. Nur aus Gründen der Lesbarkeit.
Ich habe den Werten nach eigenem Ermessen Datentypen zugewiesen. Weisen Sie die richtigen Typen zu (um grundlegende Sanitätsprüfungen durchzuführen) oder machen Sie einfach alle numeric
:
Übergeben Sie Werte für die dynamische Ausführung mit der USING
Klausel. Dies vermeidet das Hin- und Herwerfen und macht alles einfacher, sicherer und schneller.
Ich benutze einen OUT
Parameter, weil das eleganter ist und eine kürzere, klarere Syntax ermöglicht. Ein Finale RETURN
wird nicht benötigt, der Wert der OUT-Parameter wird automatisch zurückgegeben.
Beachten Sie den Vortrag über Sicherheit von @Chris und das Kapitel "Sicheres Schreiben von SECURITY DEFINER-Funktionen" im Handbuch. In meinem Design ist der einzige Injektionspunkt die Formel selbst.
Sie können für einige Parameter Standardeinstellungen verwenden , um den Aufruf weiter zu vereinfachen.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) angeben oderALTER FUNCTION foo(text) SECURITY DEFINER
a+b
in einer Textspalte in einer Tabelle gespeichert ist. Dann habe ich eine Funktion,foo(a int, b int,formula text)
wenn die Formel a + b ist. Wie kann ich die Funktion dazu bringen, tatsächlich a + b anstelle von auszuführen? Muss ich eine sehr lange case-Anweisung für alle möglichen Formeln haben und den Code in allen Segmenten wiederholen?Eine Alternative zum Speichern und anschließenden Ausführen der Formel (die, wie Chris erwähnte, Sicherheitsprobleme aufweist ) wäre eine separate Tabelle,
formula_steps
die im Wesentlichen die Variablen und Operatoren sowie die Reihenfolge enthält, in der sie ausgeführt werden. Dies wäre etwas mehr Arbeit, aber sicherer. Die Tabelle könnte folgendermaßen aussehen:Eine andere Möglichkeit wäre die Verwendung einer Bibliothek / eines Tools eines Drittanbieters zur Auswertung mathematischer Ausdrücke. Dies würde Ihre Datenbank weniger anfällig für SQL-Injection machen, aber jetzt haben Sie die möglichen Sicherheitsprobleme auf Ihr externes Tool verlagert (das möglicherweise immer noch ziemlich sicher ist).
Die letzte Option wäre das Schreiben (oder Herunterladen) einer Prozedur, die mathematische Ausdrücke auswertet. Es gibt bekannte Algorithmen für dieses Problem, daher sollte es nicht schwierig sein, Informationen online zu finden.
quelle