【问题标题】:MySQL and PHP - logical condition grouping of linked entitiesMySQL 和 PHP - 链接实体的逻辑条件分组
【发布时间】:2012-03-20 08:34:23
【问题描述】:

我有一个表条件,它定义了一些条件。 我有另一个名为规则的表,它定义了一些规则,每个规则可以有很多条件。但是,规则必须能够以这种方式对条件进行分组:

(condition1) && 
(condition2) && 
    (condition3 
    || condition4 
    || 
       (condition6 && 
       condition7)
    ) && 
(condition5)

由于这种嵌套可以随心所欲,因此我无法将这两个表与绑定表绑定,因此我正在考虑将其实际保存到规则表中: (条件1)&&(条件2)&&(条件3||条件4||(条件6&&条件7))&&(条件5) 其中“condition1”将是条件 1 的 ID,等等。

然后我会在 PHP 中解析这些信息,并以某种方式从整个字符串中创建一个逻辑检查。

这是一种好的方法,还是有更好的方法来做这种与 MySQL 的嵌套绑定?我绝对需要保留在单个规则上对 AND 和 OR 子组中的条件进行分组的能力。

【问题讨论】:

    标签: php mysql


    【解决方案1】:

    我当然会避免将您的条件存储为字符串。您必须解析该字符串才能使用它,如果您想编辑预先存在的条件(或搜索某些条件等),您可能最终会陷入困境。

    尽管@tpaksu 的解决方案存在一些缺陷,但我肯定会将其存储在某种形式的树结构中。我会建议一些类似的东西

    ID  |  Rule_ID   |  Condition_ID    |  Parent   |  Operator |
    ------------------------------------------------------------
    1   |  1         |  NULL            | NULL      |  AND      |
    2   |  1         |  1               | 1         |  NULL     |
    3   |  1         |  2               | 1         |  NULL     |
    4   |  1         |  NULL            | 1         |  OR       |
    5   |  1         |  3               | 4         |  NULL     |
    6   |  1         |  4               | 4         |  NULL     |
    7   |  1         |  NULL            | 4         |  AND      |
    8   |  1         |  6               | 7         |  NULL     |
    9   |  1         |  7               | 7         |  NULL     |
    10  |  1         |  5               | 4         |  NULL     |
    

    我认为这代表了您想要建模的原始逻辑结构。这样做的好处是您可以在树中有多个分支,其中有相同级别的条件,并且您可以跟踪每个条件属于哪个分支 - 您不必依赖表中条目的顺序。父列是指表的主 ID 列。

    你基本上有两种类型的行(这表明你可以把它分成两个表):

    • 带有运算符的行是一个“节点”,它使用两个逻辑运算符之一组合一个或多个条件/节点(但我猜您可以使用任何逻辑运算符)。这些节点没有condition_id
    • 带有 condition_id 的行代表一个条件(我已根据您的示例对它们进行编号)。这些行没有运算符,因为您不能在单个操作数上使用运算符。我没有考虑您的基本条件本身是否使用与您在问题中抽象出该细节相同的逻辑运算符。

    我不确定您是否也需要 rule_id。在此示例中,您不需要,但如果您想存储有关每个规则的元信息(例如名称/标签、有效日期范围等),您可能希望将其提取到单独的表中。它还可以更容易地在 PHP 中重构逻辑,因为 MySQL 不容易处理递归查询 AFAIAA。

    【讨论】:

    • 美丽。我已经在很大程度上分析了这一点,我喜欢它。今天和明天我会试一试,然后报告它是如何工作的。谢谢!
    • 仍在构建它上面的逻辑,一些新功能开始发挥作用,而且我将它作为一个组件解耦,以便它也可以在其他框架中使用。如果我喘口气,应该在几天内完成并测试
    • @Swader 酷。如果我的回答对你有用,你能接受吗?
    • 尚未对其进行测试,但它让我走上了正轨。当然,完成。谢谢!
    【解决方案2】:

    如果我要构建这样的东西,我可能会使用这种结构,

    ID  |  Rule_ID   |  Condition_ID    |  Level   |  Operator |
    ------------------------------------------------------------
    1   |  1         |  1               | 0        |  AND      |
    2   |  1         |  2               | 1        |  OR       |
    3   |  1         |  1               | 1        |  OR       |
    4   |  1         |  2               | 0        |  NULL     |
    

    所以它会产生:

    条件1 && (条件2 || 条件1) ||条件2

    好的,这就是我成功的:

    <?php
    
    /*(condition1) && 
    (condition2) && 
        (condition3 
        || condition4 
        || 
           (condition6 && 
           condition7)
        ) && 
    (condition5)
    
    id  | RuleID  | conditionID | level   | operator
    -------------------------------------------------
    1     1         1             1         AND
    2     1         2             1         AND
    3     1         3             2         OR
    4     1         4             2         OR
    5     1         6             3         AND
    6     1         7             3         AND
    7     1         5             1         NULL
    
    */
    
    $conditions = array(
        array(),
        array(condition=>"condition1"),
        array(condition=>"condition2"),
        array(condition=>"condition3"),
        array(condition=>"condition4"),
        array(condition=>"condition5"),
        array(condition=>"condition6"),
        array(condition=>"condition7")
    );
    
    $rules = array(
        array(RuleID=>1,conditionID=>1,level=>1,operator=>"AND"),
        array(RuleID=>1,conditionID=>2,level=>1,operator=>"AND"),
        array(RuleID=>1,conditionID=>3,level=>2,operator=>"OR"),
        array(RuleID=>1,conditionID=>4,level=>2,operator=>"OR"),
        array(RuleID=>1,conditionID=>6,level=>3,operator=>"AND"),
        array(RuleID=>1,conditionID=>7,level=>3,operator=>"AND"),
        array(RuleID=>1,conditionID=>5,level=>1,operator=>null)
    );
    
    echo "<pre>";
    //print_r($conditions);
    //print_r($rules);
    
    
    //mocking mysql ruleID filter with array_filter()
    $firstrule = array_filter($rules,function($element){return ($element["RuleID"]==1);});
    
    var_dump($firstrule);
    
    $level = 1;
    $condition_count = count($firstrule);
    foreach($firstrule as $key=>$condition){
    
        $new_level = $condition["level"];
        $next_level = $firstrule[$key+1]["level"];
        $prev_op = $firstrule[$key-1]["operator"];
    
        if($new_level!=$level){
            if($new_level>$level){
                echo str_repeat("(",$new_level-$level);
                if($condition_count > $key && $next_level < $new_level){
                    echo $conditions[$condition["conditionID"]]["condition"];
                }else{
                    echo $conditions[$condition["conditionID"]]["condition"].getOperator($condition["operator"]);
                }
            }
            else
            {
                if($condition_count > $key && $next_level < $new_level){
                    echo str_repeat(")",$level-$new_level);
                    echo getOperator($prev_op).$conditions[$condition["conditionID"]]["condition"] ;
                }else{
                    echo str_repeat(")",$level-$new_level);
                    echo $conditions[$condition["conditionID"]]["condition"] ;
                }
            }
        }else{
            if($condition_count > $key && $next_level < $new_level){        
                echo $conditions[$condition["conditionID"]]["condition"];
            }else{      
                echo $conditions[$condition["conditionID"]]["condition"].getOperator($condition["operator"]);
            }
    
        }
        $level = $new_level;
    }
    
    function getOperator($op){
        switch($op){
            case "AND": return "&&";
            case "OR" : return "||";
            case null : return "";
            default   : return "";
        }
    }
    
    ?>
    

    【讨论】:

    • 好主意,但我不明白您将如何使用这种结构构建我的示例。你能示范一下吗?
    • 这段代码的输出是:condition1&amp;&amp;condition2&amp;&amp;(condition3||condition4||(condition6&amp;&amp;condition7))&amp;&amp;condition5
    • 有趣的方法。演示代码中有一些错误是你,在 2 级 3 之后,放下 2 级,然后再放下 2 级 3,但其他方面令人印象深刻。现在我在想这样是不是有点矫枉过正,但总而言之,这是一个不错的方法,谢谢!
    • 不客气。而且我认为这有点像活动记录之类的查询构建器正在使用,但我猜他们没有这种多层次的方法。我认为值得检查他们的一些方法。
    • 毕竟我已经决定反对了。这种方法使我完全依赖于定义的 ID 顺序,并且结构本身与实际字符串(例如 condition1&&condition2&&(condition3||condition4||(condition6&&condition7))&&con‌​dition5 并没有任何不同,因为条件不是' t 真正分组 - 你只是告诉代码在哪里写什么并且没有逻辑连贯性。经过进一步检查,这基本上只是将一个字符串分解成一个表,这似乎有点矫枉过正,而不是定义逻辑单元。尽管如此,还是一次勇敢的尝试!
    猜你喜欢
    • 2015-08-23
    • 1970-01-01
    • 2014-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多