POSTGRES - 将(子)表达式错误评估为NULL

时间:2018-05-08 11:28:32

标签: sql postgresql rdbms

我在Postgres表measurements中有传感器数据,其中列idtimestamps0s1s2,... 。

此外,列(id, timestamp)上有一个索引。我想允许动态数学表达式(在下面的例子中:sin(s3)*0.1000/s5)来计算派生值。

SELECT
  timestamp,
  trunc((sin(s3) * 0.1000/s5)::numeric, 3) AS "calculated"
FROM measurements
WHERE id = 42
ORDER BY timestamp DESC
LIMIT 10000;

显然,这很容易被零和#34;错误将导致查询失败。有没有办法捕获此错误并返回,例如NULL是否会出现错误的计算值?

受启发

我已经尝试定义一个postgres函数eval_numeric(sensors int[], formula text)来解析公式并在异常时返回NULL。上面的SQL语句的第三行现在读取

trunc(eval_numeric(ARRAY[s3,s5],'sin(var1)*0.1/var2'), 3) AS "calculated"

这给出了期望的行为,但EXPLAIN ANALYZE报告的执行时间增加了20倍(~20ms - > ~400ms)。还有其他想法吗?

更新

要评估的动态表达式源自Web应用程序用户。所以上面的公式只是一个例子(可能需要检查平方根的负参数)。我宁愿有一般的错误检查可能性,并且宁愿在数学表达式中没有逻辑。这对最终用户来说会更容易,我可以验证允许的数学,例如使用数学解析器从而防止SQL注入。

2 个答案:

答案 0 :(得分:2)

你能把表达式改成这个吗?

class MsgFormatter extends AbstractDeclarativeFormatter {

    extension MsgGrammarAccess msgGrammarAccess = grammarAccess as MsgGrammarAccess

    override protected void configureFormatting(FormattingConfig c) {
        c.setLinewrap(0, 1, 2).before(SL_COMMENTRule)
        c.setLinewrap(0, 1, 2).before(ML_COMMENTRule)
        c.setLinewrap(0, 1, 1).after(ML_COMMENTRule)

        c.setLinewrap().before(definitionRule); // does not work
        c.setLinewrap(1,1,2).before(definitionRule); // does not work
        c.setLinewrap().before(fieldTypeRule); // does not work

    }
}

这是完成你想要做的事情的最简单方法。

答案 1 :(得分:1)

您还可以使用CASE表达式:

SELECT
    timestamp,
    CASE WHEN s5 != 0
         THEN trunc((sin(s3) * 0.1000/s5)::numeric, 3)
         ELSE NULL AS "calculated",
FROM measurements
WHERE id = 42
ORDER BY timestamp DESC
LIMIT 10000;

这样做的潜在好处是,您可以将值替换为您想要的任何内容,包括NULL

另一种选择,如果您不关心会触发除以零的行,则只需将s5上的检查添加到WHERE子句,然后过滤掉这些行分裂发生了。