在XML节点

时间:2016-09-10 11:38:22

标签: sql sql-server xml

我有这个SQL查询:

DECLARE @process TABLE(ID INT IDENTITY, workflowXML XML, Name nvarchar(250),Description nvarchar(MAX));
INSERT INTO @process(workflowXML,Name,Description) VALUES
('<process>
    <Event type="start" id="StartEvent_1">
      <outgoing>SequenceFlow_1uj46ib</outgoing>
    </Event>
    <Task type="service" id="Task_0uurv2v">
      <incoming>SequenceFlow_1uj46ib</incoming>
      <outgoing>SequenceFlow_051szgj</outgoing>
    </Task>
    <Task type="user" id="Task_1yh7nak">
      <incoming>SequenceFlow_051szgj</incoming>
      <TaskUsers>
        <TaskUser RoleName="myFirstRole" />
        <TaskUser RoleName="mySecondRole" />
      </TaskUsers>      
    </Task>
  </process>'
  ,'Process1'
  ,'test Process 1')
,('<process>
    <Event type="start" id="StartEvent_1" name="Start">
      <outgoing>SequenceFlow_0z7u86p</outgoing>
      <outgoing>SequenceFlow_1onkt3z</outgoing>
    </Event>
    <Task type="service" id="Task_0a7vu1x">
      <incoming>SequenceFlow_108ajnm</incoming>
      <incoming>SequenceFlow_1onkt3z</incoming>
      <outgoing>SequenceFlow_01clcmz</outgoing>
    </Task>
    <Task type="user" id="Task_00ijt4n">
      <incoming>SequenceFlow_17q1ecq</incoming>
      <incoming>SequenceFlow_0q9j3et</incoming>
      <outgoing>SequenceFlow_1ygvv8b</outgoing>
      <outgoing>SequenceFlow_02glv1g</outgoing>
    </Task>
    <Task type="service" id="Task_1rnuz4y">
      <incoming>SequenceFlow_1ygvv8b</incoming>
      <incoming>SequenceFlow_0z7u86p</incoming>
      <outgoing>SequenceFlow_108ajnm</outgoing>
      <outgoing>SequenceFlow_17q1ecq</outgoing>
      <outgoing>SequenceFlow_075iuj9</outgoing>
    </Task>                
    <Task type="user" id="Task_1d4ykor">
      <incoming>SequenceFlow_01clcmz</incoming>
      <incoming>SequenceFlow_075iuj9</incoming>
      <incoming>SequenceFlow_1djp3tu</incoming>
      <outgoing>SequenceFlow_0q9j3et</outgoing>
      <TaskUsers>
        <TaskUser RoleName="myFirstRole" />
        <TaskUser RoleName="mySecondRole" />
      </TaskUsers> 
    </Task>        
    <Task type="user" id="Task_1sembw4">
      <incoming>SequenceFlow_02glv1g</incoming>
      <outgoing>SequenceFlow_1djp3tu</outgoing>
      <TaskUsers>
        <TaskUser RoleName="myFirstRole" />
        <TaskUser RoleName="mySecondRole" />
      </TaskUsers> 
    </Task>    
</process>'
  ,'Process2'
  ,'test Process 2')
,('<process>
    <Event type="start" id="StartEvent_0bivq0x">
      <outgoing>SequenceFlow_0q5ik20</outgoing>
      <outgoing>SequenceFlow_147xk2x</outgoing>
    </Event>
    <Task type="service" id="Task_141buye">
      <incoming>SequenceFlow_0q5ik20</incoming>
      <incoming>SequenceFlow_0wg37hn</incoming>
      <outgoing>SequenceFlow_1pvpyhe</outgoing>
      <outgoing>SequenceFlow_10is4pe</outgoing>
    </Task>
    <Task type="service" id="Task_1n3p00i" >
      <incoming>SequenceFlow_147xk2x</incoming>
      <incoming>SequenceFlow_10is4pe</incoming>
      <outgoing>SequenceFlow_18ks1jr</outgoing>
      <outgoing>SequenceFlow_08gxini</outgoing>
    </Task>
    <Task type="user" id="Task_0olxqpp">
      <incoming>SequenceFlow_1pvpyhe</incoming>
      <outgoing>SequenceFlow_03eekq0</outgoing>
    </Task>
    <Task type="user" id="Task_0zjgfkf">
      <incoming>SequenceFlow_18ks1jr</incoming>
      <incoming>SequenceFlow_03eekq0</incoming>
      <outgoing>SequenceFlow_0wg37hn</outgoing>
      <TaskUsers>
            <TaskUser RoleName="mythirdRole" />
      </TaskUsers>
    </Task>
    <Task type="service" id="Task_1q71efy">
      <incoming>SequenceFlow_08gxini</incoming>
    </Task>
</process>'
  ,'Process3'
  ,'test Process 3')
;

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.query('.') AS Task
          ,prTbl.Name AS [Name]
          ,prTbl.Description AS [Description]
    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]
          ,[Description]
          ,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],[Description],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.Description,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]<>'user' 
      AND r.NodePath NOT LIKE '%| ' + nxt.Id + '%' 
      AND r.Step<=10 
)
select a.tblID as ProcessID,[Name],[Description],a.NodePath,a.Id as TaskID
from
(
    SELECT t.tblID 
      ,t.[Name] 
      ,t.Description
      ,t.NodePath
      ,t.Id
    FROM recCTE AS t
    WHERE t.[Type]='user'
        AND t.Step<=ISNULL((SELECT MIN(x.Step) FROM recCTE AS x WHERE x.tblID=t.tblID AND x.[Type]='user' AND x.NodeName='Task'),10000)  
) a
ORDER BY a.tblID

这将返回从开始事件(Task)可以看到第一个节点的Event type="start"个节点。 NodePath解释了从开始事件到目标节点的路径。 此查询的结果如下:

result1

我需要从RoleName

中选择结果的新查询
<TaskUsers>
   <TaskUser RoleName="myFirstRole" />
   <TaskUser RoleName="mySecondRole" />
</TaskUsers> 

存在于SplitbyDelimiter函数的输出中。

SplitbyDelimiter函数按,分割字符串。例如:

 select * from SplitbyDelimiter('myFirstRole,mySecondRole',',')

返回

split

我编辑了DerivedTable这样的部分:

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.query('.') AS Task
          ,prTbl.Name AS [Name]
          ,prTbl.Description AS [Description]
          --,t.c.value('@RoleName','nvarchar(max)') as [Role]
    FROM @process AS prTbl
    CROSS APPLY prTbl.workflowXML.nodes('process') AS A(pr)
    CROSS APPLY pr.nodes('*') AS B(nd)
    CROSS APPLY prTbl.workflowXML.nodes('process/Task/TaskUsers/TaskUser') AS t(c)
    where t.c.value('@RoleName','nvarchar(max)') in (select * from SplitbyDelimiter('myFirstRole,mySecondRole',','))
)

但它不正确并返回216条记录!(原因:存在额外的列,具有不同的值)。另外,我在查询结尾添加了inner join result,但这不能正常工作。

更新

我使用了这个查询:

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.query('.') AS Task
          ,prTbl.Name AS [Name]
          ,prTbl.Description AS [Description]
          ,t.c.value('@RoleName','nvarchar(max)') as [Role]
    FROM @process AS prTbl
    CROSS APPLY prTbl.workflowXML.nodes('process') AS A(pr)
    CROSS APPLY pr.nodes('*') AS B(nd)
    CROSS APPLY prTbl.workflowXML.nodes('process/Task/TaskUsers/TaskUser') AS t(c)      
)
,AllIncoming AS
(
    SELECT tblId
          ,NodeName 
          ,[Type]
          ,Id 
          ,[Name]
          ,[Description]
          ,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],[Description],[Role],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.Description,nxt.Role,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]<>'user' 
      AND r.NodePath NOT LIKE '%| ' + nxt.Id + '%' 
      AND r.Step<=10 
)
select a.tblID as ProcessID,[Name],[Description],a.NodePath,a.Id as TaskID,count(*) as records
from
(
    SELECT t.tblID 
      ,t.[Name] 
      ,t.Description
      ,t.NodePath
      ,t.Id
      ,t.Role     

    FROM recCTE AS t
    WHERE t.[Type]='user'
        AND t.Step<=ISNULL((SELECT MIN(x.Step) FROM recCTE AS x WHERE x.tblID=t.tblID AND x.[Type]='user' AND x.NodeName='Task'),10000)  
) a

INNER JOIN  [dbo].[SplitbyDelimiter]('myFirstRole,mySecondRole',',') r 
    ON r.TheField = a.Role
    group by a.tblID,[Name],[Description],a.NodePath,a.Id
    ORDER BY a.tblID

返回:  result2

此查询返回太多相同的记录,运行时间为19s !!这不是好查询。

如果有人可以解释此查询的解决方案,那将非常有用。

1 个答案:

答案 0 :(得分:1)

您的查询

  

我使用了这个查询:

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.query('.') AS Task
          ,prTbl.Name AS [Name]
          ,prTbl.Description AS [Description]
          ,t.c.value('@RoleName','nvarchar(max)') as [Role]
    FROM @process AS prTbl
    CROSS APPLY prTbl.workflowXML.nodes('process') AS A(pr)
    CROSS APPLY pr.nodes('*') AS B(nd)
    CROSS APPLY prTbl.workflowXML.nodes('process/Task/TaskUsers/TaskUser') AS t(c)    

) 

使用上一个CROSS APPLY中的错误路径。您将获得所有TaksUser元素。因此有很多行...你可以将其更改为

[...]
CROSS APPLY pr.nodes('*') AS B(nd)
OUTER APPLY nd.nodes('TaskUsers/TaskUser') AS t(c)   

收集当前节点下的所有TaskUser元素......

可是:

如果向原始结果集添加行,那么解决WorkFlow的整个方法都会中断!

您可以尝试:

将信息添加为附加列,其余部分保持不变。在我的查询中,我做了两次(TaskUsers和RoleNames),因为我真的不明白,你想用这个做什么:

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.query('.') AS Task
          ,prTbl.Name AS [Name]
          ,prTbl.Description AS [Description]
          ,nd.query('./TaskUsers/TaskUser') AS TaskUsers
          ,nd.query('./TaskUsers/TaskUser').query('for $rn in /TaskUser return string($rn/@RoleName)').value('.','nvarchar(max)') AS RoleNames
    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]
          ,[Description]
          ,TaskUsers
          ,RoleNames
          ,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],[Description],TaskUsers,RoleNames,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.Description,nxt.TaskUsers,nxt.RoleNames,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]<>'user' 
      AND r.NodePath NOT LIKE '%| ' + nxt.Id + '%' 
      AND r.Step<=10 
)
select a.tblID as ProcessID,[Name],[Description],TaskUsers,RoleNames,a.NodePath,a.Id as TaskID
from
(
    SELECT t.tblID 
      ,t.[Name] 
      ,t.Description
      ,t.TaskUsers
      ,t.RoleNames
      ,t.NodePath
      ,t.Id
    FROM recCTE AS t
    WHERE t.[Type]='user'
        AND t.Step<=ISNULL((SELECT MIN(x.Step) FROM recCTE AS x WHERE x.tblID=t.tblID AND x.[Type]='user' AND x.NodeName='Task'),10000)  
) a
ORDER BY a.tblID

结果如下所示

enter image description here

您想要对这些附加信息做什么,您想要应用的任何过滤器都应该作为最后一步完成......

但是 - 说实话 - 你已经达到了SQL-Server的极限......这不是正确的工具。迟早会有更多的业务逻辑要实现......如果必须在SQL Server中完成,你可能会想到一个CLR方法......