2016-08-20 102 views
1

我有一個Process表:跟蹤XML節點在SQL Server中的SQL Server這樣的流

enter image description here

workflowXML列有值是這樣的:

樣本1:

<process> 
     <Event type="start" id="StartEvent_1"> 
     <outgoing>SequenceFlow_0h5l5vu</outgoing> 
     </Event> 
     <Flow type="sequence" 
      id="SequenceFlow_0h5l5vu" 
      sourceRef="StartEvent_1" 
      targetRef="Task_1qc93ha"/> 
     <Flow type="sequence" 
      id="SequenceFlow_120gi3p" 
      sourceRef="Task_1qc93ha" 
      targetRef="Task_0x1pjee"/> 
     <Task type="service" id="Task_1qc93ha"> 
     <incoming>SequenceFlow_0h5l5vu</incoming> 
     <outgoing>SequenceFlow_120gi3p</outgoing> 
     </Task> 
     <Task type="user" id="Task_0x1pjee"> 
     <incoming>SequenceFlow_120gi3p</incoming> 
     </Task> 
</process> 

樣品2:

<process id="Process_1" isExecutable="false"> 
     <Event type="start" id="StartEvent_142xowk"> 
     <outgoing>SequenceFlow_03yocm5</outgoing> 
     </Event> 
     <Flow type="sequence" 
      id="SequenceFlow_03yocm5" 
      sourceRef="StartEvent_142xowk" 
      targetRef="Task_12g1q69"/> 
     <Task type="user" id="Task_0x1pjee"> 
      <incoming>SequenceFlow_120gi3p</incoming> 
     </Task> 
     <Task type="user" id="Task_12g1q69"> 
     <incoming>SequenceFlow_03yocm5</incoming> 
     </Task> 
</process> 

我想用Flow節點跟蹤節點流。例如,我需要查詢從開始事件(Event type="start")開始返回的Task節點,並在用戶類型爲(type="user")的Task中完成。該查詢在樣本1返回Task節點與id="Task_0x1pjee"樣品2回報Task節點與id="Task_12g1q69"

我覺得這個查詢有這樣的結構:

nodesflow

EDIT1

樣品3具有節點,使他們能夠有一個以上的傳入或傳出。

<process id="Process_1" isExecutable="false"> 
    <Event type="start" id="StartEvent_1"> 
     <outgoing>SequenceFlow_0qn7l4p</outgoing> 
    </Event> 
    <Flow type="sequence" id="SequenceFlow_0qnhn9s" sourceRef="Task_1jfd878" targetRef="Task_15id5tl"/> 
    <Task type="service" id="Task_1jfd878"> 
     <incoming>SequenceFlow_0qn7l4p</incoming> 
     <outgoing>SequenceFlow_0qnhn9s</outgoing> 
     <outgoing>SequenceFlow_10zjx6e</outgoing> 
    </Task> 
    <Flow type="sequence" id="SequenceFlow_0qn7l4p" sourceRef="StartEvent_1" targetRef="Task_1jfd878"/> 
    <Flow type="sequence" id="SequenceFlow_10zjx6e" sourceRef="Task_1jfd878" targetRef="Task_0qnuy6q"/> 
    <Task type="user" id="Task_0qnuy6q"> 
     <incoming>SequenceFlow_10zjx6e</incoming> 
     <incoming>SequenceFlow_0xiah51</incoming> 
    </Task> 
    <Task type="service" id="Task_15id5tl"> 
     <incoming>SequenceFlow_0qnhn9s</incoming> 
     <outgoing>SequenceFlow_0xiah51</outgoing> 
    </Task> 
    <Flow type="sequence" id="SequenceFlow_0xiah51" sourceRef="Task_15id5tl" targetRef="Task_0qnuy6q"/> 
    </process> 

如果有人能夠解釋此查詢的解決方案將是非常有益的。

感謝。

+0

難道你不想將數據存儲在合適的表中而不是XML中嗎? –

+0

@EvaldasBuinauskas不,我需要以XML格式存儲數據。 –

+0

根據使用的方法,XML已經有方法來檢索重複節點(''// '')或返回水平列中的特定重複節點的方法('''') –

回答

1

我希望我得到這個正確:

你開始類型=「開始」和走的層次結構,其中 - 數據是下一個節點的ID。該行具有未定義的深度,並且應該以具有type =「user」的節點結束。

你的第二個例子得到了2個任務,其中type =「user」,但其中只有一個被引用爲鏈上較高節點的out-data。

我的示例將使用額外的EXISTS子句過濾第二個。

第一個CTE DerivedTable由您也可能使用的查詢組成。它將以表格形式提供完整的信息。

第二個CTE是遞歸的,從開始開始並遍歷鏈。列號等級是鏈的順序。

第三個CTE會添加一個反向排序,因爲您似乎只對最後一個項目感興趣。你可能會WHERE RevRank=1

DECLARE @process TABLE(ID INT IDENTITY, workflowXML XML); 
INSERT INTO @process(workflowXML) VALUES 
('<process> 
     <Event type="start" id="StartEvent_1"> 
     <outgoing>SequenceFlow_0h5l5vu</outgoing> 
     </Event> 
     <Flow type="sequence" 
      id="SequenceFlow_0h5l5vu" 
      sourceRef="StartEvent_1" 
      targetRef="Task_1qc93ha"/> 
     <Flow type="sequence" 
      id="SequenceFlow_120gi3p" 
      sourceRef="Task_1qc93ha" 
      targetRef="Task_0x1pjee"/> 
     <Task type="service" id="Task_1qc93ha"> 
     <incoming>SequenceFlow_0h5l5vu</incoming> 
     <outgoing>SequenceFlow_120gi3p</outgoing> 
     </Task> 
     <Task type="user" id="Task_0x1pjee"> 
     <incoming>SequenceFlow_120gi3p</incoming> 
     </Task> 
</process>') 
,('<process id="Process_1" isExecutable="false"> 
     <Event type="start" id="StartEvent_142xowk"> 
     <outgoing>SequenceFlow_03yocm5</outgoing> 
     </Event> 
     <Flow type="sequence" 
      id="SequenceFlow_03yocm5" 
      sourceRef="StartEvent_142xowk" 
      targetRef="Task_12g1q69"/> 
     <Task type="user" id="Task_0x1pjee"> 
      <incoming>SequenceFlow_120gi3p</incoming> 
     </Task> 
     <Task type="user" id="Task_12g1q69"> 
     <incoming>SequenceFlow_03yocm5</incoming> 
     </Task> 
</process>'); 

- 這得到這是查詢:

WITH DerivedTable AS 
(
    SELECT prTbl.ID AS tblID 
      ,nd.value('local-name(.)','nvarchar(max)') AS [Name] 
      ,nd.value('@type','nvarchar(max)') AS [Type] 
      ,nd.value('@id','nvarchar(max)') AS Id 
      ,COALESCE(nd.value('@sourceRef','nvarchar(max)') 
        ,nd.value('(incoming)[1]','nvarchar(max)')) AS [In] 
      ,COALESCE(nd.value('@targetRef','nvarchar(max)') 
        ,nd.value('(outgoing)[1]','nvarchar(max)')) AS [Out] 
    FROM @process AS prTbl 
    CROSS APPLY prTbl.workflowXML.nodes('process') AS A(pr) 
    CROSS APPLY pr.nodes('*') AS B(nd) 
) 
,recCTE AS 
(
    SELECT tblID,[Name],[Type],Id,[In],[Out],1 AS [Rank] 
    FROM DerivedTable 
    WHERE [Type]='start' 

    UNION ALL 

    SELECT x.tblID,x.[Name],x.[Type],x.Id,x.[In],x.[Out],r.[Rank]+1 
    FROM recCTE AS r 
    INNER JOIN DerivedTable AS x ON x.[Id]=r.[Out] 
            AND EXISTS(SELECT 1 
               FROM DerivedTable AS y 
               WHERE y.tblID=x.tblID AND y.[Out]=x.[Id]) 
) 
,ReverseRank AS 
(
    SELECT * 
      ,ROW_NUMBER() OVER(PARTITION BY tblID ORDER BY [Rank] DESC) AS RevRank 
    FROM recCTE 
) 
SELECT * 
FROM ReverseRank 
ORDER BY tblID,[Rank] 

結果(您的預計產量爲RevRank = 1):

+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| tblID | Rank | RevRank | Name | Type  | Id     | In     | Out     | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 1  | 1 | 5  | Event | start | StartEvent_1   | NULL     | SequenceFlow_0h5l5vu | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 1  | 2 | 4  | Flow | sequence | SequenceFlow_0h5l5vu | StartEvent_1   | Task_1qc93ha   | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 1  | 3 | 3  | Task | service | Task_1qc93ha   | SequenceFlow_0h5l5vu | SequenceFlow_120gi3p | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 1  | 4 | 2  | Flow | sequence | SequenceFlow_120gi3p | Task_1qc93ha   | Task_0x1pjee   | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 1  | 5 | 1  | Task | user  | Task_0x1pjee   | SequenceFlow_120gi3p | NULL     | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 2  | 1 | 3  | Event | start | StartEvent_142xowk | NULL     | SequenceFlow_03yocm5 | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 2  | 2 | 2  | Flow | sequence | SequenceFlow_03yocm5 | StartEvent_142xowk | Task_12g1q69   | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 
| 2  | 3 | 1  | Task | user  | Task_12g1q69   | SequenceFlow_03yocm5 | NULL     | 
+-------+------+---------+-------+----------+----------------------+----------------------+----------------------+ 

UPDATE :您的評論

我用XM測試了我的查詢l在您的評論:

INSERT INTO @process(workflowXML) VALUES 
('<process> 
    <Event type="start" id="e1"> 
    <outgoing>s1</outgoing> 
    </Event> 
    <Flow type="sequence" id="s1" sourceRef="e1" targetRef="t1" /> 
    <Flow type="sequence" id="s3" sourceRef="t1" targetRef="t2" /> 
    <Task type="user" id="t3"> 
    <incoming>s2</incoming> 
    </Task> 
    <Task type="user" id="t1"> 
    <incoming>s1</incoming> 
    <outgoing>s3</outgoing> 
    </Task> 
    <Flow type="sequence" id="s2" sourceRef="t2" targetRef="t3" /> 
    <Task type="service" id="t2"> 
    <incoming>s3</incoming> 
    <outgoing>s2</outgoing> 
    </Task> 
</process>'); 

這是結果

+-------+-------+----------+----+------+------+------+---------+ 
| tblID | Name | Type  | Id | In | Out | Rank | RevRank | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Event | start | e1 | NULL | s1 | 1 | 7  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Flow | sequence | s1 | e1 | t1 | 2 | 6  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Task | user  | t1 | s1 | s3 | 3 | 5  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Flow | sequence | s3 | t1 | t2 | 4 | 4  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Task | service | t2 | s3 | s2 | 5 | 3  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Flow | sequence | s2 | t2 | t3 | 6 | 2  | 
+-------+-------+----------+----+------+------+------+---------+ 
| 1  | Task | user  | t3 | s2 | NULL | 7 | 1  | 
+-------+-------+----------+----+------+------+------+---------+ 

如果我理解其中的邏輯正確我查詢工作只是罰款:

  • 事件ID = E1點S1
  • 流程s1指向t1
  • 任務t1指向s3
  • 流S3點到t2
  • 任務T2點s2的
  • 流S2點至t3
  • 任務T3是結束

,我看到不同的唯一的事情,是一個事實,即任務t1已經是type =「user」。如果你想要的 - 在任何情況下 - 排名最高的用戶任務,你可能會帶走ReverseRank -CTE和設置最終SELECT

SELECT t.* 
FROM recCTE AS t 
WHERE t.[Rank]<=ISNULL((SELECT MIN(x.[Rank]) FROM recCTE AS x WHERE x.tblID=t.tblID AND x.[Type]='user' AND x.[Name]='Task'),999) 
ORDER BY t.tblID,t.[Rank] 

現在任務T1將是最後的結果,其隨後所有的行列被過濾掉。

+0

非常感謝您的回答,我發佈了一個答案。請看這個。 –

+0

@AliSoltani你試過我的方法嗎?你的問題 - 如果它返回你所需要的 - 沒問題,但是是一個*程序*方法。使用SQL時,總是建議遵循*​​ set-based *路徑......我的查詢將返回整個流程鏈,並且應該比您的查詢更快。你的方法的主要問題是,你一遍又一遍地讀取XML中的一段數據。 'CURSOR'和'WHILE'-loops應該儘可能避免......我建議嘗試我的方法,並決定什麼更適合你... – Shnugo

+1

我測試你的查詢。我認爲這樣更好。但有一個問題。有時,節點中有幾個傳入和傳出。你的代碼在這種情況下是否正常工作? –

0

我用這個查詢解決了這個問題。如果有更好的查詢,請高興指出。

--================== @tempProcess(result)========================= 
declare @tempProcess table 
(
ID int, 
FirstTaskID nvarchar(max) 
) 
--=============================================================== 
declare @currentType nvarchar(max) 
declare @FirstUserTaskID nvarchar(max) 
declare @outgoing nvarchar(max) 
declare @elementID nvarchar(max) 
--================================================================ 
declare @ID int 
declare @WorkflowXML xml 
declare cur CURSOR LOCAL for 
    select ID, WorkflowXML from Process 
open cur 

fetch next from cur into @ID, @WorkflowXML 

while @@FETCH_STATUS = 0 BEGIN  

set @currentType = '$$$$$'--defult value 
set @elementID = '$$$$$'--defult value 

select @outgoing = 
(
    select p.WorkflowXML.value('(process/Event[@type=''start'']/outgoing)[1]','nvarchar(max)') 
    from Process as p 
    where ID = @ID 
) 

--====================== while(Tracking flow) ======================== 
while (@currentType != 'user') 
begin 
    ------- Get target element with Flow Id (outgoing)----------------- 
    select @elementID = (
     select t.c.value('@id','nvarchar(max)') 
     from Process as p 
      cross apply p.WorkflowXML.nodes('process/*') AS t(c) 
     where ID = @ID 
     and 
     t.c.value('incoming[1]','nvarchar(max)') = @outgoing 
    ) 
    -------------- Get Type of current element ------------------------ 
    select @currentType = 
    (
    select t.c.value('@type','nvarchar(max)') 
     from Process as p 
      cross apply p.WorkflowXML.nodes('process/*') AS t(c) 
     where ID = @ID 
     and 
     t.c.value('@id','nvarchar(max)') = @elementID 
    ) 
    -------------- Get outgoing of current element ---------------------  
    select @outgoing = 
    (
    select t.c.value('(outgoing)[1]','nvarchar(max)') 
     from Process as p 
      cross apply p.WorkflowXML.nodes('process/*') AS t(c) 
     where ID = @ID 
     and 
     t.c.value('@id','nvarchar(max)') = @elementID 
    ) 
    --------------------------------------------------------------- 
end 
--========================= End while ======================== 
if(@elementID != '$$$$$') 
    begin 
     set @FirstUserTaskID = @elementID  
     -- Insert to @tempProcess 
     INSERT INTO @tempProcess 
     SELECT @ID,@FirstUserTaskID 
    end 

    --select @FirstUserTaskID 
    fetch next from cur into @ID,@WorkflowXML 
END 

select * from @tempProcess 

close cur 
deallocate cur