Multiple goal search on XML nodes in SQL Server

匿名 (未验证) 提交于 2019-12-03 02:35:01

问题:

I have a Process table in SQL Server like this:

workflowXML column has values like this:

sample1 (ProcessID=1)

workflowXML of sample1:

<process>     <Event type="start" id="StartEvent_1" name="Start">       <outgoing>SequenceFlow_0z7u86p</outgoing>       <outgoing>SequenceFlow_1onkt3z</outgoing>     </Event>     <task type="" id="Task_0a7vu1x" name="D">       <incoming>SequenceFlow_108ajnm</incoming>       <incoming>SequenceFlow_1onkt3z</incoming>       <outgoing>SequenceFlow_01clcmz</outgoing>     </task>     <task type="goal" id="Task_00ijt4n" name="B">       <incoming>SequenceFlow_17q1ecq</incoming>       <incoming>SequenceFlow_0q9j3et</incoming>       <outgoing>SequenceFlow_1ygvv8b</outgoing>       <outgoing>SequenceFlow_02glv1g</outgoing>     </task>     <task type="" id="Task_1rnuz4y" name="A">       <incoming>SequenceFlow_1ygvv8b</incoming>       <incoming>SequenceFlow_0z7u86p</incoming>       <outgoing>SequenceFlow_108ajnm</outgoing>       <outgoing>SequenceFlow_17q1ecq</outgoing>       <outgoing>SequenceFlow_075iuj9</outgoing>     </task>                     <task type="goal" id="Task_1d4ykor" name="E">       <incoming>SequenceFlow_01clcmz</incoming>       <incoming>SequenceFlow_075iuj9</incoming>       <incoming>SequenceFlow_1djp3tu</incoming>       <outgoing>SequenceFlow_0q9j3et</outgoing>     </task>             <task type="goal" id="Task_1sembw4" name="C">       <incoming>SequenceFlow_02glv1g</incoming>       <outgoing>SequenceFlow_1djp3tu</outgoing>     </task>     </process> 

sample2 (ProcessID=2)

workflowXML of sample2:

<process id="Process_1" isExecutable="false">     <Event type="start" id="StartEvent_0bivq0x" name="Start">       <outgoing>SequenceFlow_0q5ik20</outgoing>       <outgoing>SequenceFlow_147xk2x</outgoing>     </Event>     <task type="" id="Task_141buye" name="A">       <incoming>SequenceFlow_0q5ik20</incoming>       <incoming>SequenceFlow_0wg37hn</incoming>       <outgoing>SequenceFlow_1pvpyhe</outgoing>       <outgoing>SequenceFlow_10is4pe</outgoing>     </task>     <task type="" id="Task_1n3p00i" name="C">       <incoming>SequenceFlow_147xk2x</incoming>       <incoming>SequenceFlow_10is4pe</incoming>       <outgoing>SequenceFlow_18ks1jr</outgoing>       <outgoing>SequenceFlow_08gxini</outgoing>     </task>     <task type="goal" id="Task_0olxqpp" name="B">       <incoming>SequenceFlow_1pvpyhe</incoming>       <outgoing>SequenceFlow_03eekq0</outgoing>     </task>     <task type="goal" id="Task_0zjgfkf" name="D">       <incoming>SequenceFlow_18ks1jr</incoming>       <incoming>SequenceFlow_03eekq0</incoming>       <outgoing>SequenceFlow_0wg37hn</outgoing>     </task>     <task type="" id="Task_1q71efy" name="E">       <incoming>SequenceFlow_08gxini</incoming>     </task> </process> 

Edit1 (Add Sample3)

sample3 (ProcessID=3)

workflowXML of sample3:

<process>   <Event type="start" id="StartEvent_1" name="Start">     <outgoing>SequenceFlow_01rkkhj</outgoing>   </Event>   <task type="" id="Task_1jixk79" name="A">     <incoming>SequenceFlow_01rkkhj</incoming>     <incoming>SequenceFlow_1tszkq8</incoming>     <outgoing>SequenceFlow_0v8wuqu</outgoing>     <outgoing>SequenceFlow_14u6fh7</outgoing>     <outgoing>SequenceFlow_1q4991g</outgoing>   </task>   <task type="" id="Task_0xwvhuo" name="B">     <incoming>SequenceFlow_0v8wuqu</incoming>     <outgoing>SequenceFlow_15fmkbq</outgoing>     <outgoing>SequenceFlow_0x4ykgp</outgoing>     <outgoing>SequenceFlow_0f4gpf1</outgoing>   </task>   <task type="goal" id="Task_0qsvlob" name="G">     <incoming>SequenceFlow_0qse1xk</incoming>     <incoming>SequenceFlow_16a0qvv</incoming>   </task>   <task type="goal" id="Task_0wtjftd" name="E">     <incoming>SequenceFlow_14u6fh7</incoming>     <incoming>SequenceFlow_0z3qle8</incoming>     <outgoing>SequenceFlow_0vg7sax</outgoing>     <outgoing>SequenceFlow_0qse1xk</outgoing>   </task>   <task type="" id="Task_0c85e6p" name="F">     <incoming>SequenceFlow_0x4ykgp</incoming>     <incoming>SequenceFlow_17k5zfg</incoming>     <outgoing>SequenceFlow_16a0qvv</outgoing>     <outgoing>SequenceFlow_0z3qle8</outgoing>   </task>   <task type="" id="Task_164ihwt" name="D">     <incoming>SequenceFlow_0q9hqs6</incoming>     <incoming>SequenceFlow_1q4991g</incoming>     <outgoing>SequenceFlow_17k5zfg</outgoing>   </task>   <task type="goal" id="Task_032o8jx" name="C">     <incoming>SequenceFlow_15fmkbq</incoming>     <incoming>SequenceFlow_0vg7sax</incoming>     <outgoing>SequenceFlow_0q9hqs6</outgoing>     <outgoing>SequenceFlow_1tszkq8</outgoing>   </task>   <task type="goal" id="Task_0fsibap" name="H">     <incoming>SequenceFlow_0f4gpf1</incoming>   </task> </process> 

I need to find goal nodes from start node that:

there is a path for them from start and there are no goal nodes in this path.

Result of query in process table with sample1 and sample2 is like this:

+-------+----------+-------------+ |  ID   | nodeName |    nodeID   | +-------+----------+-------------+ |   1   |    B     |Task_00ijt4n | +-------+----------+-------------+ |   1   |    E     |Task_1d4ykor | +-------+----------+-------------+ |   2   |    B     |Task_0olxqpp | +-------+----------+-------------+ |   2   |    D     |Task_0zjgfkf | +-------+----------+-------------+ 

It would be very helpful if someone could explain solution for this query.

Thanks.

回答1:

I was curious to solve this with a set-based recursive approach as I do not like procedural coding in T-SQL. Hope you like it :-)

DECLARE @process TABLE(ID INT IDENTITY, workflowXML XML); INSERT INTO @process(workflowXML) VALUES ('<process>     <Event type="start" id="StartEvent_1" name="Start">       <outgoing>SequenceFlow_0z7u86p</outgoing>       <outgoing>SequenceFlow_1onkt3z</outgoing>     </Event>     <task type="" id="Task_0a7vu1x" name="D">       <incoming>SequenceFlow_108ajnm</incoming>       <incoming>SequenceFlow_1onkt3z</incoming>       <outgoing>SequenceFlow_01clcmz</outgoing>     </task>     <task type="goal" id="Task_00ijt4n" name="B">       <incoming>SequenceFlow_17q1ecq</incoming>       <incoming>SequenceFlow_0q9j3et</incoming>       <outgoing>SequenceFlow_1ygvv8b</outgoing>       <outgoing>SequenceFlow_02glv1g</outgoing>     </task>     <task type="" id="Task_1rnuz4y" name="A">       <incoming>SequenceFlow_1ygvv8b</incoming>       <incoming>SequenceFlow_0z7u86p</incoming>       <outgoing>SequenceFlow_108ajnm</outgoing>       <outgoing>SequenceFlow_17q1ecq</outgoing>       <outgoing>SequenceFlow_075iuj9</outgoing>     </task>                     <task type="goal" id="Task_1d4ykor" name="E">       <incoming>SequenceFlow_01clcmz</incoming>       <incoming>SequenceFlow_075iuj9</incoming>       <incoming>SequenceFlow_1djp3tu</incoming>       <outgoing>SequenceFlow_0q9j3et</outgoing>     </task>             <task type="goal" id="Task_1sembw4" name="C">       <incoming>SequenceFlow_02glv1g</incoming>       <outgoing>SequenceFlow_1djp3tu</outgoing>     </task>     </process>') ,('<process id="Process_1" isExecutable="false">     <Event type="start" id="StartEvent_0bivq0x" name="Start">       <outgoing>SequenceFlow_0q5ik20</outgoing>       <outgoing>SequenceFlow_147xk2x</outgoing>     </Event>     <task type="" id="Task_141buye" name="A">       <incoming>SequenceFlow_0q5ik20</incoming>       <incoming>SequenceFlow_0wg37hn</incoming>       <outgoing>SequenceFlow_1pvpyhe</outgoing>       <outgoing>SequenceFlow_10is4pe</outgoing>     </task>     <task type="" id="Task_1n3p00i" name="C">       <incoming>SequenceFlow_147xk2x</incoming>       <incoming>SequenceFlow_10is4pe</incoming>       <outgoing>SequenceFlow_18ks1jr</outgoing>       <outgoing>SequenceFlow_08gxini</outgoing>     </task>     <task type="goal" id="Task_0olxqpp" name="B">       <incoming>SequenceFlow_1pvpyhe</incoming>       <outgoing>SequenceFlow_03eekq0</outgoing>     </task>     <task type="goal" id="Task_0zjgfkf" name="D">       <incoming>SequenceFlow_18ks1jr</incoming>       <incoming>SequenceFlow_03eekq0</incoming>       <outgoing>SequenceFlow_0wg37hn</outgoing>     </task>     <task type="" id="Task_1q71efy" name="E">       <incoming>SequenceFlow_08gxini</incoming>     </task> </process>'); 

--the query

WITH DerivedTable AS (     SELECT prTbl.ID AS tblID           ,nd.value('local-name(.)','nvarchar(max)') AS NodeName           ,nd.value('@type','nvarchar(max)') AS [Type]           ,nd.value('@id','nvarchar(max)') AS Id           ,nd.value('@name','nvarchar(max)') AS [Name]           ,nd.query('.') AS Task     FROM @process AS prTbl     CROSS APPLY prTbl.workflowXML.nodes('process') AS A(pr)     CROSS APPLY pr.nodes('*') AS B(nd) ) ,AllIncoming AS (     SELECT tblId           ,NodeName            ,[Type]           ,Id            ,[Name]           ,i.value('.','nvarchar(max)') AS [Target]      FROM DerivedTable     CROSS APPLY Task.nodes('task/incoming') AS A(i)     WHERE NodeName='task' ) ,recCTE AS (     SELECT tblID,NodeName,[Type],Id,[Name],Task,1 AS Step,' | ' +CAST(Id AS NVARCHAR(MAX)) AS NodePath     FROM DerivedTable      WHERE [Type]='start'      UNION ALL      SELECT nxt.tblID,nxt.NodeName,nxt.[Type],nxt.Id,nxt.[Name],nxt.Task,r.Step+1,r.NodePath + ' | ' + nxt.Id     FROM recCTE AS r     INNER JOIN DerivedTable AS nxt ON nxt.Id IN(SELECT x.Id                                                  FROM AllIncoming AS x                                                  WHERE x.[Target] IN (SELECT o.value('.','nvarchar(max)')                                                                      FROM r.Task.nodes('*/outgoing') AS A(o)                                                                     )                                                 )     WHERE r.[Type]<>'goal'        AND r.NodePath NOT LIKE '%| ' + nxt.Id + '%'        AND r.Step<=10 --add an appropriate depth limit to avoid recusion-depth error ) SELECT t.tblID        ,t.[Name]        ,t.NodePath       ,t.Step       ,t.Id FROM recCTE AS t WHERE t.[Type]='goal'   AND t.Step<=ISNULL((SELECT MIN(x.Step) FROM recCTE AS x WHERE x.tblID=t.tblID AND x.[Type]='goal' AND x.NodeName='task'),999) ORDER BY t.tblID,t.Step 

The result

tblID   Name    NodePath                                          Step  Id   1     B      | StartEvent_1 | Task_1rnuz4y | Task_00ijt4n        3    Task_00ijt4n   1     E      | StartEvent_1 | Task_1rnuz4y | Task_1d4ykor        3    Task_1d4ykor   1     E      | StartEvent_1 | Task_0a7vu1x | Task_1d4ykor        3    Task_1d4ykor   2     D      | StartEvent_0bivq0x | Task_1n3p00i | Task_0zjgfkf  3    Task_0zjgfkf   2     B      | StartEvent_0bivq0x | Task_141buye | Task_0olxqpp  3    Task_0olxqpp 

You find more than two results for tblID=1 as there are differing paths leading to the same goal node.

UPDATE Your example 3

My first attempt finds the shortest path to a goal. Any goal, which is reached with a longer path, was filtered. This is easy to change:

Let the final WHERE find the shortest path to a specific node by adding the Id:

WHERE t.[Type]='goal'   AND t.Step<=ISNULL((SELECT MIN(x.Step)                        FROM recCTE AS x                        WHERE x.tblID=t.tblID                         AND x.Id=t.Id                          AND x.[Type]='goal' AND x.NodeName='task'),999) 

This returns for all three examples:

+-------+------+----------------------------------------------------------------------------+------+--------------+ | tblID | Name | NodePath                                                                   | Step | Id           | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 1     | B    | | StartEvent_1 | Task_1rnuz4y | Task_00ijt4n                               | 3    | Task_00ijt4n | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 1     | E    | | StartEvent_1 | Task_1rnuz4y | Task_1d4ykor                               | 3    | Task_1d4ykor | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 1     | E    | | StartEvent_1 | Task_0a7vu1x | Task_1d4ykor                               | 3    | Task_1d4ykor | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 2     | B    | | StartEvent_0bivq0x | Task_141buye | Task_0olxqpp                         | 3    | Task_0olxqpp | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 2     | D    | | StartEvent_0bivq0x | Task_1n3p00i | Task_0zjgfkf                         | 3    | Task_0zjgfkf | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 3     | E    | | StartEvent_1 | Task_1jixk79 | Task_0wtjftd                               | 3    | Task_0wtjftd | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 3     | C    | | StartEvent_1 | Task_1jixk79 | Task_0xwvhuo | Task_032o8jx                | 4    | Task_032o8jx | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 3     | H    | | StartEvent_1 | Task_1jixk79 | Task_0xwvhuo | Task_0fsibap                | 4    | Task_0fsibap | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 3     | G    | | StartEvent_1 | Task_1jixk79 | Task_0xwvhuo | Task_0c85e6p | Task_0qsvlob | 5    | Task_0qsvlob | +-------+------+----------------------------------------------------------------------------+------+--------------+ | 3     | G    | | StartEvent_1 | Task_1jixk79 | Task_164ihwt | Task_0c85e6p | Task_0qsvlob | 5    | Task_0qsvlob | +-------+------+----------------------------------------------------------------------------+------+--------------+ 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!