2016-08-20 307 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