我需要将公式存储到表中(用户必须管理这些公式)并使用它来计算,使用数据库的信息,一些字段并存储它们。公式随时间而变化,没有常数变量。
公式可以是:
(a*b+c)/24.5
或
2456.789*a+b²-c+23.58*d
我使用PostgreSQL作为数据库系统。我该如何正确实现?
答案 0 :(得分:1)
如果公式是有效的SQL表达式,那么你可以使用PL / PgSQL和EXECUTE
来运行它们......但这样做是非常不安全的,因为用户可以调用任意SQL函数。
如果它们不是有效的SQL表达式,那么您必须确定它们将的内容,并使用适当的表达式处理器。
因此,在有效地回答这个问题之前,您需要定义公式可以做什么和不能做什么,用户的信任程度等等。
我只想要纯粹的表达,我可能会使用SymPy和PL / Pythonu,但我不喜欢缺少沙盒版本。 e.g:
yum install sympy
(或您首选的apt-get
等效物)然后
CREATE OR REPLACE FUNCTION eval_user_expr(userexpr text)
RETURNS float8
LANGUAGE plpythonu
AS $$
from sympy.parsing.sympy_parser import parse_expr
return float(parse_expr(userexpr));
$$;
或者如果你想把变量作为输入:
CREATE OR REPLACE FUNCTION eval_user_expr(userexpr text, varnames text[], varvalues float8[])
RETURNS float8
LANGUAGE plpythonu
AS $$
from sympy.parsing.sympy_parser import parse_expr
return float(parse_expr(userexpr, dict(zip(varnames, varvalues))));
然后使用:
regress=# SELECT eval_user_expr('2456.789*a+b**2-c+23.58*d', ARRAY['a', 'b', 'c', 'd'], ARRAY[3, 4, 9, 42]);
eval_user_expr
----------------
8367.727
(1 row)
当然,这非常笨拙。它将所有值都视为双精度浮点(Python' s float
是双精度值)。我没有看到一个简单的解决方案,因为Python的hetrogenous-typed dict
没有匹配的原生PostgreSQL类型。你可以通过json或类似的,但它仍然非常有限。
这还要求您使用SymPy的表达式语法。例如,b²
无法理解:
regress=# SELECT eval_user_expr('b²', ARRAY['b'], ARRAY[1]);
ERROR: SyntaxError: invalid syntax (<string>, line 1)
CONTEXT: Traceback (most recent call last):
PL/Python function "eval_user_expr", line 3, in <module>
return float(parse_expr(userexpr, dict(zip(varnames, varvalues))));
PL/Python function "eval_user_expr", line 756, in parse_expr
PL/Python function "eval_user_expr", line 690, in eval_expr
PL/Python function "eval_user_expr"
并且您必须使用Python表单b**2
:
regress=# SELECT eval_user_expr('b**2', ARRAY['b'], ARRAY[1]);
eval_user_expr
----------------
1
(1 row)
PL / Lua,依靠它的沙箱并直接运行Lua表达式。
使用现有表达式解析和执行库的自定义C扩展(如果可以找到一个好的扩展)。也许GNU libmatheval
(我从未尝试过或测试过它)。
PL / perl如果你能找到合适的Perl表达式解析库。或PL / V8,如果你能找到一个JavaScript。或者您愿意运行pl/perl
沙盒并运行用户提供的原始Perl表达式。
PL / Java,如果您非常绝望,可以找到满足您Java需求的东西。