MySQL和PHP-链接实体的逻辑条件分组

MySQL和PHP-链接实体的逻辑条件分组,php,mysql,Php,Mysql,我有一个表conditions,它定义了一些条件。 我有另一个名为rules的表,它定义了一些规则,每个规则可以有许多条件。但是,规则必须能够以这种方式对条件进行分组: (condition1) && (condition2) && (condition3 || condition4 || (condition6 && condition7) ) && (co

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

(condition1) && 
(condition2) && 
    (condition3 
    || condition4 
    || 
       (condition6 && 
       condition7)
    ) && 
(condition5)
由于这种嵌套可以任意深入,我无法用绑定表绑定这两个表,因此我考虑将其实际保存到规则表中: (条件1)和(条件2)和(条件3、条件4、条件6和条件7)和(条件5) 其中“条件1”将是条件1的ID,等等

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


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

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

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 "";
    }
}

?>

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

虽然@tpaksu的解决方案有一些缺陷,但我肯定会将其存储在某种形式的树结构中。我会提出类似的建议

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

您基本上有两种类型的行(这向我建议您可以将其拆分为两个表):

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

我不确定你是否也需要规则id。在本例中,您不需要这样做,但如果您想存储关于每个规则的元信息(例如名称/标签、有效日期范围等),您可能需要将其提取到单独的表中。由于MySQL不容易处理AFAIAA的递归查询,因此在PHP中重建逻辑可能更容易。

好主意,但我不知道如何使用这种结构构建我的示例。你能演示一下吗?这个代码的输出是:
condition1&&condition2&&(condition3 | | | condition4 | |(condition6&&condition7))&&condition5
有趣的方法。在演示代码中有一些错误是你,在2级3秒之后,放下一级2,然后再次放下2级3秒,但在其他方面令人印象深刻。现在我在想这样做是否有点过分,但总而言之,这是一个不错的方法,谢谢!不客气。我认为这有点像active record这样的查询生成器所使用的,但我想他们没有这种多层次的方法。我认为他们的一些方法值得一试。毕竟我决定反对。这种方法使我完全依赖于定义的ID顺序,并且结构本身与实际字符串(如condition1&&condition2&&(condition3 | | | condition4 | |(condition6&&condition7))&&con)没有任何区别‌​第五,因为条件并没有真正分组——你只是告诉代码在哪里写什么,没有逻辑上的一致性。进一步检查后,这基本上只是将一个字符串分解成一个表,这似乎有些过分,而不是定义逻辑单元。不过,这是一次勇敢的尝试!美丽的。我已经在很大程度上分析了这一点,我喜欢它。我将在今天和明天试一试,并汇报它是如何工作的。谢谢仍然在构建上面的逻辑,一些新特性开始发挥作用,另外我将它作为一个组件进行解耦,以便它也可以在其他框架中使用。如果我染上感冒,应该在几天内完成并测试breather@Swader酷。如果我的答案对你有用,你能接受吗?我还没有测试过,但它让我走上了正确的轨道。当然,完成了。谢谢
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     |