![](/img/trans.png)
[英]How to store and evaluate dynamic expressions in MySQL(or any other SQL)
[英]How to write Postgres Dynamic SQL statement to evaluate expressions stored in rows?
我正在使用 PostgreSQL,我需要编写 SQL 语句来解决这两个表的以下问题:
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 |
例如,有 2 个规则行:
id | key | sql_expression
------+-----------------+-----------------------
1 | amount | > 100
2 | merchant_name | ~* '.*amazon.*'
给定一个事务 id(让我们称匹配的事务行"this_transaction"
),我想找到所有规则行,其中每个规则行的键和 sql_expression 值从 "this_transaction" 构造的条件计算为真。 在这个例子中,如果"this_transaction".amount > 100
,我想要规则 1,如果"this_transaction".merchant_name ~* '.*amazon.*'
我想要规则 2
我不知道如何写这个 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 表达式或以其他方式解决问题有任何建议,请告诉我。 感谢您的时间和考虑。
PS 在我们的情况下,我们将有许多规则行,这些行会被许多用户不断创建、更新和删除。 请提出一个支持这个灵活系统的解决方案。 谢谢你。
create or replace function eval_bool(data record, key text, expr text)
returns bool stable strict language plpgsql
as $$
declare
result bool;
begin
execute 'select $1.' || key || ' ' || expr into result using data;
return result;
end $$;
-- test:
select relname
from pg_class as t
where eval_bool(t, 'relname', 'like ''%class%''');
┌───────────────────────────────────┐
│ relname │
├───────────────────────────────────┤
│ pg_class_oid_index │
│ pg_class_relname_nsp_index │
│ pg_class_tblspc_relfilenode_index │
│ pg_opclass_am_name_nsp_index │
│ pg_opclass_oid_index │
│ pg_class │
│ pg_opclass │
└───────────────────────────────────┘
现在应该很简单:
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" type
。
那是棘手的地形。
您可以编写一个返回 function 的集合,该集合采用事务 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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.