将逻辑表达式存储到RDBMS中的结构

时间:2016-05-29 16:11:15

标签: php mysql sql rdbms logical-operators

考虑以下变量由播放器分析器服务生成:

    level = 6;
    errors = 4;
    score = 12;
    ...

我们有一些规则和信息:

 1. errors == 0 AND level > 5 : Senior player
 2. score == 10 OR errors == 3: Border line player
 3. score > 10 AND score < 13: Not good, just passed
 4. ...

现在我们应该打印正确的消息。

另一个例子:考虑以下变量由 food 分析器服务生成:

    fruit = 2;
    coca = 6;
    ...

我们有一些规则和信息:

 1. fruit == 0 : Consider buying some fruits
 2. coca == 0: That's healthy
 3. ...

现在我们应该打印正确的消息。

我应该如何在像MySQL这样的RDBMS中保存规则和消息,以便查询和查找消息变得容易。

最糟糕的方法是将规则保存在一列中,将消息保存在另一列中,然后加载每个记录以使用主机编程语言进行测试。

enter image description here enter image description here

你能为这种情况建议一个更好的方法吗?当我们有几千条消息时,这不是一个好方法,我们需要一种方法来过滤数据库端的消息。

3 个答案:

答案 0 :(得分:0)

我已经创建了一个快速的ERD来演示我最初的设计方式: enter image description here

所有这些列和表的含义是什么?

<强> PROPERTY_NAME

这包含可以对其进行检查的所有内容的列表。

  • property_id - 主键
  • property_name - 具有针对其存储的值的项的文本值。例如“错误”,“水平”,“水果”。

<强>运算符

包含用于每个属性的不同运算符的列表。

  • operator_id - 主键。
  • operator_symbol - 检查值时使用的符号。我不确定实际符号是否是存储在这里的最佳值,但它可以工作。例如“==”,“&gt;”,“&gt; =”。

<强> rule_message

存储正在显示的实际消息。

  • rule_message_id - 主键
  • message - 要显示的消息文本。例如“高级玩家”,“考虑购买水果”。

<强> operator_property

这是所有其他三个表之间的连接表,包含您的规则和逻辑。

  • property_operator_id - 主键。它被称为代理键 - 如果您愿意,可以排除此列,并将PK设为(property_id,operator_id,rule_message_id),如果这是您喜欢的话。
  • property_id - 正在使用的property_name记录(例如“错误”的ID)
  • operator_id - 正在使用的运营商记录(例如“==”的ID)
  • rule_message_id - 正在使用的rule_message(例如“高级玩家”的ID)
  • check_value - 针对运营商的属性检查的值。例子是6,4,12。

如何使用此设计: *您可以将所有属性和运算符添加到表中。 *要查找要为场景显示的消息,例如检查播放器的显示内容:

SELECT rn.rule_message_id, rm.message
FROM rule_message rm
INNER JOIN operator_property op ON rm.rule_message_id = op.rule_message_id
INNER JOIN property_Name pn ON op.property_id = pn.property_id
INNER JOIN operator o ON op.operator_id = o.operator_id
WHERE 1=1
AND (
    pn.property_name = "errors"
    AND pn.operator_symbol = "=="
    AND op.check_value = 0
)
AND (
    pn.property_name = "level"
    AND pn.operator_symbol = "5"
    AND op.check_value = 5
)

理想情况下,此查询将返回1行。如果返回0,则不应用任何消息。如果它返回2或更多,则表示它不能完全符合您的一个标准,因此没有任何消息适用。

希望这有帮助!我以前written articles on desigining databases,我能给你的最好的建议就是弄清楚数据的用途,看起来你已经拥有了。

另外,如果你能想到更好的桌子名称,那就去吧 - 这只是一个快速的设计来说明这一点。

答案 1 :(得分:0)

一般来说,这种规则解释不是直接在数据库中完成的,最终会在像check_rules_against_data这样的解释器中完成,这绝对没问题。

直接在一个或多个php文件中编写所有规则是很常见的(当然由一些代码包围if ($rule) { echo $message; })。它通常比每次动态评估每个规则更快(并且请记住,数据库也必须这样做)。如何编码过滤器取决于您的需求;您可以坚持您的规则格式,您可以只显示完整的PHP代码并让用户编辑它,您可以将它们拆分并使用数据库设计来实现,例如验证变量是否存在(参见下面的扩展rule_term - 表或completeitpro的答案)。所有这些都可以正常工作。

如果您愿意,或者您想测试它,您可以在数据库中进行一些预选。有很多方法可以做到这一点,并且有很多方法可以针对特殊情况进行优化,这将大大取决于你真正想做的事情,所以我只想描述一种方式,给你一个想法。

你的变量看起来你会有很多,但所有这些变量都是整数(因此拥有一个焦炭并不意味着:Items[x]='COCA',但coca=1),所以你可以放他们和表格中的规则如下:

可变

variableid | variablename | variabletype
----------------------------------------
1          | errors       | 1
2          | level        | 1 
3          | score        | 1 

user_variable

userid     | variableid  | valueint  
-------------------------------------
1          | 1           | 0         
1          | 2           | 6         
1          | 3           | 10         
2          | 1           | 3         
2          | 3           | 10        
3          | 1           | 0         
3          | 2           | 6         
3          | 3           | 10         
4          | 1           | 0         
4          | 2           | 5         

规则

ruleid | mincount | message
---------------------------
1      | 2        | Senior player          -> AND (2 terms have to fit)
2      | 1        | Border line player     -> OR (any 1 term can fit)

rule_term

ruleid | variableid | minvalueint | maxvalueint
-----------------------------------------------
1      | 1          | 0           | 0            -> error == 0
1      | 2          | 6           | 9999         -> level > 5
2      | 1          | 3           | 3            -> error == 3
2      | 3          | 10          | 10           -> score == 10

根据这些规则,您现在可以预先选择点击的规则:

select user_variable.userid, rule.ruleid, count(*) as cntfulfilled, 
       max(rule.mincount) as mincnt, max(rule.message) as message
from rule_term
join rule
on rule_term.ruleid = rule.ruleid
join user_variable 
on rule_term.variableid = user_variable.variableid
and rule_term.minvalueint <= user_variable.valueint 
and rule_term.maxvalueint >= user_variable.valueint
group by user_variable.userid, rule.ruleid
having count(*) >= max(rule.mincount);

这应该计入每个用户和每个规则,满足此规则的子节点数。如果我没有弄错的话,这应该是:

userid | ruleid | cntfulfilled | mincnt | message
--------------------------------------------------
1      | 1      | 2            | 2      | Senior player
1      | 2      | 1            | 1      | Border line player
2      | 2      | 2            | 1      | Border line player
3      | 1      | 2            | 2      | Senior player

要表达ANDmincnt应该是所有子条目的数量,对于OR,它将是1.使用普通AND或{{构建规则1}},这已经是完整的测试。

对于更复杂的规则,您必须能够在php中重新创建规则以将其放入检查功能中。你可以,例如将其编码在如下表格中:

扩展OR - 表格:

rule_term

我使用cond = 1 :(,cond = 2 :),cond = 3:NOT,cond = 4:AND,cond = 5:OR。 (有更好的方法对它进行编码,例如只表达逻辑并将其分组到嵌套的ruleid | pos | cond | var.id | min | max -------------------------------------------- 3 | 1 | 1 | 0 | 0 | 0 -> ( 3 | 2 | 0 | 1 | 1 | 1 -> error == 1 3 | 3 | 4 | 2 | 5 | 5 -> AND level == 5 3 | 4 | 2 | 0 | 0 | 0 -> ) 3 | 5 | 5 | 3 | 10 | 10 -> OR score == 10 - 子组中,但这里不会改进任何东西)。

这将允许您仍然预先选择可能适合的规则,以获得您之后必须在php中分析的规则(您不能再使用mincnt,因为即使只是AND,mincnt也将为1,而不仅仅是error == 1)。

您可以添加更多内容:您可以添加字符串变量类型(向score == 10valuestr添加列user_variable并调整联接)或标记为&#39 ; NOT&#39;,如果您能够在rule_term-table中的行中表达它们,则可以向连接添加更复杂的copndition(例如,组合2个变量并在双连接中检查2个变量)。

它有点困难,但您可能希望使用左连接和一些额外的逻辑来比较不存在的变量(例如,如果您不想设置变量rule_term适合所有人,只适用于有(或有)可乐的用户。

如果你想使用水平变量(一列中固定数量的变量),你应该对规则项(每个变量的最小/最大列)做同样的事情,并调整连接以检查每个变量列。

这只是一个普遍的想法,您显然有很多替代方法可以做到这一点,最佳选择和优化将在很大程度上取决于您的实际需求,并花更多时间考虑您的数据库设计(或如何生成动态php文件)稍后会减少挫折(很多)或提高速度(很多)。我会再次提醒你,测试生成动态php文件的选项 - 这通常要快得多。

答案 2 :(得分:0)

这是规则系统的经典案例,可能不会在数据库中实现。我把一个java库(Rulette)组合在一起,它可以做到这一点。

基本上,您可以通过创建rule_system表并向其中插入条目来设置它,并创建包含条目的规则输入表(级别,错误,分数)。根据您的样本,级别和错误似乎是“值”&#39;类型的同时得分&#39;似乎是一个&#39; RANGE&#39;类型。

现在,您可以创建规则表(&#39; player_rules {id,level,error,score}&#39;)来配置您的所有规则,并将它们映射到输出表格中的条目(&#39; player_message { id,message}&#39;)。

很高兴!!

RuleSystem rs = new RuleSystem("player-rule-system");
Rule r = rs.getRule(new HasMap<>(){"level":level, "error: : error, "score" : score});