如何编写Postgres动态SQL语句来计算存储在行中的表达式?
我正在使用PostgreSQL,我需要编写SQL语句来解决这两个表的以下问题:如何编写Postgres动态SQL语句来计算存储在行中的表达式?,sql,postgresql,dynamic-sql,Sql,Postgresql,Dynamic Sql,我正在使用PostgreSQL,我需要编写SQL语句来解决这两个表的以下问题: Table "Transaction" Column | Type | Collation | Nullable | Default ----------------+--------------------------------+---------
Table "Transaction"
Column | Type | Collation | Nullable | Default
----------------+--------------------------------+-----------+----------+-------------------
id | text | | not null |
amount | integer | | not null |
merchant_name | text | | not null |
Table "Rule"
Column | Type | Collation | Nullable | Default
----------------+--------------------------------+-----------+----------+-------------------
id | text | | not null |
key | text | | not null |
sql_expression | text | | not null |
例如,有两个规则行:
id | key | sql_expression
------+-----------------+-----------------------
1 | amount | > 100
2 | merchant_name | ~* '.*amazon.*'
给定一个事务id(让我们调用匹配的事务行“this\u Transaction”
),我希望找到所有规则行,其中由“this\u Transaction”构造的条件的每个规则行的键和sql\u表达式值计算为true。在本例中,如果“本次交易”.amount>100,则需要规则1,如果“本次交易”.merchant\u name~*'.*亚马逊。*'
我不知道怎么写这个WHERE表达式。下面提供了一个不工作的SQL伪代码
SELECT "Rule".* FROM "Rule",
(
SELECT "Transaction".*
FROM "Transaction"
WHERE "Transaction".id = $1
) AS "this_transaction"
WHERE "this_transaction".("Rule".key) "Rule".sql_expression;
$1 = 'some_transaction_id'
如果有人对如何写WHERE表达式或用其他方式解决问题有任何建议,请让我知道。谢谢你的时间和考虑
另外,在我们的情况下,我们会有许多规则行,这些规则行会被许多用户不断地创建、更新和删除。请建议一个支持此灵活系统的解决方案。谢谢。这是一个棘手的地形
您可以编写一个集合返回函数,该函数接受事务ID,迭代规则,如果存在具有给定ID和规则表达式的事务,则返回规则
CREATE FUNCTION transaction_rule
(_transaction transaction.id%TYPE)
RETURNS SETOF rule
AS
$$
DECLARE
_exists boolean;
_rule rule;
BEGIN
FOR _rule IN (SELECT *
FROM rule) LOOP
EXECUTE format(concat('SELECT EXISTS (SELECT *',
' FROM transaction',
' WHERE id = $1',
' AND %I %s);'),
_rule.key,
_rule.sql_expression)
USING _transaction
INTO _exists;
IF _exists THEN
RETURN NEXT _rule;
END IF;
END LOOP;
END;
$$
LANGUAGE plpgsql;
但这可能会表现得很糟糕。它很容易出错,任何可以更改规则的人都可能会滥用它进行SQL注入攻击
如果需要将规则存储在数据库中而不是应用程序中,您可能可以使用事务视图并将规则添加为列。大致如下:
CREATE VIEW transaction_rules
AS
SELECT t.*,
amount > 100 rule_1,
merchant_name ~* '.*amazon.*' rule_2
FROM transaction t;
现在应该很简单:
SELECT "Rule".* FROM "Rule",
(
SELECT "Transaction".*
FROM "Transaction"
WHERE "Transaction".id = $1
) AS "this_transaction"
WHERE eval_bool("this_transaction"::"Transaction", "Rule".key, "Rule".sql_expression);
请注意,第一个值(data
)应该是注册类型的值。因此,我们需要将子查询结果强制转换为“Transaction”类型
,这将很难使用语句来实现。更典型的是,您只需要一些固定数量的语句,这些语句涵盖了您的所有预期逻辑。谢谢您的建议。我想使用CREATE视图
方法,但在本例中,您在CREATE视图中硬编码了2条规则。在我们的情况下,许多规则行可能会被不同的用户不断创建、更新和删除。有没有办法在创建视图中包含所有规则行?@AndyShih:视图的列可能有一些限制。但这应该很高。也许你应该研究一下,看看你是否会超过它。我尝试了你的解决方案,但得到了错误:无法识别记录数据类型
中的“金额”列,所以我将数据类型更改为交易
,这就成功了!谢谢大家!@是的,我明白了。子查询似乎存在问题。但是,您可以不更改函数的参数类型而强制转换值:。。。其中eval_bool(“此事务”):“事务”、“规则”.key、“规则”.sql_表达式)代码>它使函数更通用(可用于任何表)是的,这更好!非常感谢。
SELECT "Rule".* FROM "Rule",
(
SELECT "Transaction".*
FROM "Transaction"
WHERE "Transaction".id = $1
) AS "this_transaction"
WHERE eval_bool("this_transaction"::"Transaction", "Rule".key, "Rule".sql_expression);