2014-02-07 70 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