2014-02-07 71 views
0

我有一个小型的DSL,用户可以在这里为某些操作表达某些条件。现在我需要在sql server上解决这些条件。在Sql Server中执行树

条件中的节点是AND/OR/atom,其中AND/OR是二进制表达式,原子是标识符==操作数,其中==是唯一的操作符。

所以我在Sql Server中创建了下列表来存储树。

CREATE TABLE [dbo].[Condition]([Id] [hierarchyid], [Order] [int] NULL, 
[NodeType] [nchar](10),[Identifier] [nvarchar](50) ,[Operand] [nvarchar](255) NULL) 

是否有任何方式来行走这棵树,并在sql语句中评估它的节点?我可以在编译代码的C#中执行此操作,但是我在sql中思考它。我需要它在SQL中,因为数据的筛选必须发生在SQL中。

例如,如果条件是

(T=="T1" || T=="T2") && (R=="R1" || R =="R2") || T=="T3" 

表看起来像

Id  Order NodeType Identifier Operand  Id.ToString() 
------------------------------------------------------------------------------------ 
0x  NULL  OR   NULL  NULL  /
0x58 1  AND   NULL  NULL  /1/ 
0x5AC0 1  OR   NULL  NULL  /1/1/ 
0x5AD6 1  Expr  T   T1   /1/1/1/ 
0x5ADA 2  Expr  T   T2   /1/1/2/ 
0x5B40 2  OR   NULL  NULL  /1/2/ 
0x5B56 1  Expr  R   R1   /1/2/1/ 
0x5B5A 2  Expr  R   R2   /1/2/2/ 
0x68 2  Expr  T   T3   /2/ 
+0

你能张贴例如“条件”和相应的表记录? –

+0

@DourHighArch请看更新 –

回答

1

因为我们不知道项的值,直到我们对其进行评估,我们需要从这样做自下而上。在代码中,我们有一个堆栈可以跟踪我们的位置。我并不是真的想要建立一个堆栈,所以我从下到上评估了所有的术语。

我试图用CTE做到这一点,但失败了。我无法在CTE的递归成员中获得这两个术语。因此,我必须编写自己的循环。

有一个表格,其中包含所有中间值@I。每个循环我们都更接近树的顶部。当我们有了根节点的值时,我们就完成了。我们还将清除不必要的行,最后我们只有一行。

这里的模式:

CREATE TABLE [dbo].[Condition](
    [Id] hierarchyid, 
    [Order] [int] NULL, 
    [NodeType] [nchar](10), 
    [Identifier] [nvarchar](50), 
    [Operand] [nvarchar](255) NULL); 

insert Condition (id, "Order", NodeType, Identifier, Operand) values 
(0x, null, 'OR', null, null), 
(0x58, 1, 'AND', null, null), 
(0x5ac0, 1, 'OR', null, null), 
(0x5ad6, 1, 'Expr', 'T', 'T1'), 
(0x5ada, 2, 'Expr', 'T', 'T2'), 
(0x5b40, 2, 'OR', null, null), 
(0x5b56, 1, 'Expr', 'R', 'R1'), 
(0x5b5a, 2, 'Expr', 'R', 'R2'), 
(0x68, 2, 'Expr', 'T', 'T3'); 

这里的假设@T@R就是我们寻找的标识代码:

declare @T varchar(max) = 'T1'; 
declare @R varchar(max) = 'R1'; 

declare @I table (
    id hierarchyId, 
    "order" int, 
    value bit 
); 

insert @I (id, "order", value) 
select id, "order", case when operand = 
     case when identifier = 'T' then @T when identifier = 'R' then @R end 
     then 1 else 0 end 
from condition 
where nodetype = 'Expr'; 

while not exists (select * from @I where id = 0x) begin 
    insert @I (id, "order", value) 
    select node.id, node."order", 
    case 
     when nodetype = 'AND' then 
     case when L.value = 1 and R.value = 1 then 1 else 0 end 
     when nodetype = 'OR' then 
     case when L.value = 1 or R.value = 1 then 1 else 0 end 
    end 
    from condition node 
    join @I L on L.id.GetAncestor(1) = node.id and L."order" = 1 
    join @I R on R.id.GetAncestor(1) = node.id and R."order" = 2 

    delete from @I where id.GetAncestor(1) in (select id from @I) 
end 


select *, id.ToString() from @I 

这里的小提琴:http://sqlfiddle.com/#!6/8e5cc/1