PostgreSQL 9.1データベースがあり、その一部がエージェントの手数料を処理しています。各エージェントは、彼らが得る手数料の計算式を自分自身に持っています。エージェントごとのコミッションを生成する機能を持っていますが、エージェント数が増えると使えなくなります。非常に長いケースステートメントと繰り返しコードを実行することを余儀なくされたため、私の機能が非常に大きくなりました。
すべての数式には定数変数があります:
d ..その月の稼働日数 r ..獲得した新しいノード数 l ..ロイヤルティスコア s ..サブエージェント手数料 b。 。基本レート i ..獲得した収益
式は次のようになります。
d*b+(l*4+r)+(i/d)+s
各エージェントは、HR部門と支払い式を交渉します。では、式をエージェントテーブルに格納して、テーブルから式を取得して値で変換し、金額を計算するだけの小さな関数のようにできますか?
数式は次のようになります。
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');
確認してください、あなたの翻訳は健全です。 regexp式 の説明:
\ 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によるセキュリティに関する講義 と、章 "SECURITY DEFINER関数の安全な書き込み" を検討してください。私の設計では、注入の単一ポイントは式自体です。
一部のパラメーターのデフォルト を使用して、呼び出しをさらに簡略化できます。
セキュリティに関する考慮事項については、こちらをよくお読みください。基本的に、関数に任意のSQLを挿入しようとしています。したがって、非常に制限された権限を持つユーザーの下でこれを実行する必要があります。
ユーザーを作成し、ユーザーからすべての権限を取り消します。これを行う場合と同じdbでpublicに権限を付与しないでください。
式を評価する関数を作成し、security definer
そして所有者をその制限されたユーザーに変更します。
式を前処理してから、上で作成したeval()関数に渡します。必要であれば、別の関数でこれを行うことができます。
繰り返しになりますが、これはセキュリティに重大な影響を及ぼします。
編集:簡単なサンプルコード(テストされていませんが、ドキュメントに従っている場合はそこにアクセスできます):
CREATE OR REPLACE FUNCTION eval_numeric(text) returns numeric language plpgsql security definer immutable as
$$
declare retval numeric;
begin
execute $e$ SELECT ($1)::numeric$e$ into retval;
return retval;
end;
$$;
ALTER FUNCTION eval_numeric OWNER TO jailed_user;
CREATE OR REPLACE FUNCTION foo(expression text, a numeric, b numeric) returns numeric language sql immutable as $$
select eval(regexp_replace(regexp_replace($1, 'a', $2, 'g'), 'b', '$3', 'g'));
$$; -- can be security invoker, but eval needs to be jailed.
単に式を格納してから実行する代わりの方法(これは Chrisが言及しているように、セキュリティの問題があります )formula_steps
と呼ばれる別のテーブルを作成することになります。それらが実行される順序。これはもう少し作業になりますが、より安全です。テーブルは次のようになります。
formula_steps ------------- formula_step_id formula_id(FK、エージェントテーブルによって参照される) input_1 input_2 演算子(演算子記号を直接格納したくない場合は、許可された演算子のテーブルへのIDにすることもできます) シーケンス
別のオプションは、サードパーティのライブラリ/ツールを使用して数式を評価することです。これにより、データベースがSQLインジェクションの影響を受けにくくなりますが、考えられるセキュリティの問題を外部ツールにシフトしました(まだかなり安全かもしれません)。
最後のオプションは、数式を評価するプロシージャを作成(またはダウンロード)することです。この問題には既知のアルゴリズムがあるため、オンラインで情報を見つけるのは難しくありません。