sql查询varchar(max)列,根据其子子元素

时间:2016-02-25 15:51:51

标签: sql-server xml tsql xpath xquery

我正在尝试搜索存储在varchar(max)列中的xml数据 下面是varchar列中找到的一个xml数据字符串的示例 在这个例子中,我将使用名为@dsName的sql变量,当它的值与@dsName匹配时,它将与节点匹配,并且它从属于节点或从属节点。您之间或之前的节点可能会有所不同。

   <business_process>
<ProcessDefinition name="dawns test">
    <StartState name="START" uniqueId="Node3304">
        <Transition name="Node4532" to="Node4532"/>
    </StartState>
    <EndState name="END4694" uniqueId="Node4694"/>
    <User name="Node4532" uniqueId="Node4532">
        <Description>test</Description>
        <Distribution config-type="field" type="CommonQueueDistribution">
            <Priority>0</Priority>
            <AutoCompleteJob>false</AutoCompleteJob>
            <GroupId>Admin</GroupId>
            <UseAttendance>false</UseAttendance>
            <UseShifts>false</UseShifts>
            <NotifyActors>false</NotifyActors>
        </Distribution>
        <DocFinityTask type="DocFinityTask">
            <description>read e-mail and approve or deny</description>
            <help/>
            <required>false</required>
            <redoable>false</redoable>
            <condition/>
            <properties>
                <undoable>true</undoable>
            </properties>
        </DocFinityTask>
        <DocFinityTask type="SimpleFormTask">
            <description>lob lookup</description>
            <help/>
            <required>false</required>
            <redoable>true</redoable>
            <condition/>
            <properties>
                <autoRun>true</autoRun>
                <form>
                    <title>lob</title>
                    <formElement>
                        <type>Combobox</type>
                        <variable>lob</variable>
                        <tooltip>lob lookup</tooltip>
                        <label>lob</label>
                        <required>false</required>
                        <prepopulateValues>
                            <datasourceName>lob lookup</datasourceName>
                        </prepopulateValues>
                        <userEnter>true</userEnter>
                        <dataType>STRING</dataType>
                    </formElement>
                </form>
            </properties>
        </DocFinityTask>
        <Transition name="Node128795" to="Node128795"/>
    </User>
    <Server name="Node128795" uniqueId="Node128795">
        <Description/>
        <Event type="node-enter">
            <Action type="SetProcessInstancePropertyAction" config-type="field">
                <description>Whatever</description>
                <propertyName>source</propertyName>
                <datasourceName>get datasource list</datasourceName>
            </Action>
        </Event>
        <Transition name="Node4694" to="END4694"/>
    </Server>
    <Server name="Node250" uniqueId="Node250">
      <Description />
      <Event type="node-enter">
        <Action type="SetProcessInstancePropertyAction" config-type="field">
          <description>Whatever</description>
          <propertyName>source</propertyName>
          <datasourceName>stump</datasourceName>
        </Action>
      </Event>
      <Transition name="Node4694" to="END4694" />
    </Server>
</ProcessDefinition>
<Layout>
    <annotations/>
    <nodes>
        <node name="START" uniqueId="Node3304" type="startNode" text="START" x="184.5" y="135.5" width="25" height="25"/>
        <node name="END4694" uniqueId="Node4694" type="endNode" text="END4694" x="588.5" y="137.5" width="25" height="25"/>
        <node name="Node4532" uniqueId="Node4532" type="userNode" text="Node4532" info="false" x="296" y="135" width="150" height="50"/>
        <node name="Node128795" uniqueId="Node128795" type="serverNode" text="Node128795" info="false" x="286" y="244" width="150" height="50"/>
    </nodes>
    <edges>
        <edge originNode="Node3304" targetNode="Node4532" text="" sketch="arrow"/>
        <edge originNode="Node4532" targetNode="Node128795" text="" sketch="arrow"/>
        <edge originNode="Node128795" targetNode="Node4694" text="" sketch="arrow"/>
    </edges>
</Layout>

这是我在尝试加载到XML数据类型的变量时使用的选择的示例。 varchar(max)列名称是XML

DECLARE @xml XML=
            (SELECT [XML] 
             FROM ProcessModels 
             WHERE [XML] LIKE '%<datasourceName>' + @dsName + '%' 
                    and [status] = 'ACTIVE')

如果将SQL加载到表变量中并且已经使用要搜索的字符串设置了@dsName变量,那么SQL select是相同的。

在这个例子中,我想找到每个服务器节点和/或用户节点的名称,当它有一个值为&#39;获取数据源列表的节点时。字符串&#39;获取数据源列表&#39;已经存在于变量@dsName。

以下查询让我走了一半。

select sn.value('@name', 'varchar(100)') AS ServerNodes
from @xml.nodes('/business_process/ProcessDefinition/Server')  AS ServerNodes(sn)

现在我需要弄清楚如何限制Server。@ name返回到子节点// datasourceName值等于sql:variable的那些。

这有效:

SELECT ServerNode.value('@name','varchar(max)') AS ServerNode
FROM @xml.nodes('/business_process/ProcessDefinition') AS ProcessDefinition(pd)
OUTER APPLY pd.nodes('Server[Event//datasourceName=sql:variable("@searchVariable")]') AS The(ServerNode)
WHERE ServerNode.value('@name','varchar(max)') IS NOT NULL

SELECT UserNode.value('@name','varchar(max)') AS UserNode
FROM @xml.nodes('/business_process/ProcessDefinition') AS ProcessDefinition(pd)
OUTER APPLY pd.nodes('User[DocFinityTask//datasourceName=sql:variable("@searchVariable")]') AS The(UserNode)
WHERE UserNode.value('@name','varchar(max)') IS NOT NULL

3 个答案:

答案 0 :(得分:2)

您的朋友是sql:variable(),如果您的搜索值来自表格列,也会有sql:column()

根据您的评论,我使用类型为varchar的XML列模拟了一个表。 SELECT将首先使用CROSS APPLY将其转换为&#34;真实&#34; XML,然后.exist()用于获取满足您条件的行,最后返回/User/@name的值。

如果您将查找变量更改为&#34;另一个&#34;您将找到其他XML,其他字符串将返回空白。

作为XPath的示例,我给出了三个返回相同的查询。这取决于您的XML ......

DECLARE @tbl TABLE(ID INT IDENTITY, YourXMLAsVarchar VARCHAR(MAX));
INSERT INTO @tbl VALUES 
('<User name="First Node" uniqueId="1332">
    <Task type="Form">
      <properties>
        <form>
          <formElement>
            <populateValues>
              <source>lookup</source>
            </populateValues>
          </formElement>
        </form>
      </properties>
    </Task>
   </User>')
,('<User name="First Node" uniqueId="1332">
    <Task type="Form">
      <properties>
        <form>
          <formElement>
            <populateValues>
              <source>another</source>
            </populateValues>
          </formElement>
        </form>
      </properties>
    </Task>
   </User>');


--Search for "lookup"
DECLARE @SearchingFor VARCHAR(100)='lookup';

--Search with full path
SELECT x.value('(/User/@name)[1]','varchar(max)')
FROM @tbl AS tbl
CROSS APPLY(SELECT CAST(YourXMLAsVarchar AS XML)) AS a(x)
WHERE x.exist('/User/Task/properties/form/formElement/populateValues/source[.=sql:variable("@SearchingFor")]')=1

--shorter, if there is not other "source" element this could be muddled up with...
SELECT x.value('(/User/@name)[1]','varchar(max)')
FROM @tbl AS tbl
CROSS APPLY(SELECT CAST(YourXMLAsVarchar AS XML)) AS a(x)
WHERE x.exist('//source[.=sql:variable("@SearchingFor")]')=1

--even shorter, if your lookup string won't be anywhere else an element's value
SELECT x.value('(/User/@name)[1]','varchar(max)')
FROM @tbl AS tbl
CROSS APPLY(SELECT CAST(YourXMLAsVarchar AS XML)) AS a(x)
WHERE x.exist('//*[.=sql:variable("@SearchingFor")]')=1

答案 1 :(得分:2)

因为我的第一个答案已经非常拥挤......

通过这种方式,您可以从XML中获取用户和服务器的数据。如果将@searchVariable设置为不存在的datasourceName-value,则用户数据仍然存在,但Server数据将为NULL。试试吧!

DECLARE @xml XML=
'<business_process>
  <ProcessDefinition name="dawns test">
    <StartState name="START" uniqueId="Node3304">
      <Transition name="Node4532" to="Node4532" />
    </StartState>
    <EndState name="END4694" uniqueId="Node4694" />
    <User name="Node4532" uniqueId="Node4532">
      <Description>test</Description>
      <Distribution config-type="field" type="CommonQueueDistribution">
        <Priority>0</Priority>
        <AutoCompleteJob>false</AutoCompleteJob>
        <GroupId>Admin</GroupId>
        <UseAttendance>false</UseAttendance>
        <UseShifts>false</UseShifts>
        <NotifyActors>false</NotifyActors>
      </Distribution>
      <DocFinityTask type="DocFinityTask">
        <description>read e-mail and approve or deny</description>
        <help />
        <required>false</required>
        <redoable>false</redoable>
        <condition />
        <properties>
          <undoable>true</undoable>
        </properties>
      </DocFinityTask>
      <DocFinityTask type="SimpleFormTask">
        <description>lob lookup</description>
        <help />
        <required>false</required>
        <redoable>true</redoable>
        <condition />
        <properties>
          <autoRun>true</autoRun>
          <form>
            <title>lob</title>
            <formElement>
              <type>Combobox</type>
              <variable>lob</variable>
              <tooltip>lob lookup</tooltip>
              <label>lob</label>
              <required>false</required>
              <prepopulateValues>
                <datasourceName>lob lookup</datasourceName>
              </prepopulateValues>
              <userEnter>true</userEnter>
              <dataType>STRING</dataType>
            </formElement>
          </form>
        </properties>
      </DocFinityTask>
      <Transition name="Node128795" to="Node128795" />
    </User>
    <Server name="Node128795" uniqueId="Node128795">
      <Description />
      <Event type="node-enter">
        <Action type="SetProcessInstancePropertyAction" config-type="field">
          <description>Whatever</description>
          <propertyName>source</propertyName>
          <datasourceName>get datasource list</datasourceName>
        </Action>
      </Event>
      <Transition name="Node4694" to="END4694" />
    </Server>
  </ProcessDefinition>
  <Layout>
    <annotations />
    <nodes>
      <node name="START" uniqueId="Node3304" type="startNode" text="START" x="184.5" y="135.5" width="25" height="25" />
      <node name="END4694" uniqueId="Node4694" type="endNode" text="END4694" x="588.5" y="137.5" width="25" height="25" />
      <node name="Node4532" uniqueId="Node4532" type="userNode" text="Node4532" info="false" x="296" y="135" width="150" height="50" />
      <node name="Node128795" uniqueId="Node128795" type="serverNode" text="Node128795" info="false" x="286" y="244" width="150" height="50" />
    </nodes>
    <edges>
      <edge originNode="Node3304" targetNode="Node4532" text="" sketch="arrow" />
      <edge originNode="Node4532" targetNode="Node128795" text="" sketch="arrow" />
      <edge originNode="Node128795" targetNode="Node4694" text="" sketch="arrow" />
    </edges>
  </Layout>
</business_process>';

DECLARE @searchVariable VARCHAR(100)='get datasource list';

SELECT ServerNode.value('@name','varchar(max)') AS ServerName
      ,ServerNode.value('@uniqueId','varchar(max)') AS ServerId
      ,pd.value('User[1]/@name','varchar(max)') AS UserName
      ,pd.value('User[1]/@uniqueId','varchar(max)') AS UserId
FROM @xml.nodes('/business_process/ProcessDefinition') AS ProcessDefinition(pd)
OUTER APPLY pd.nodes('Server[Event/Action/datasourceName=sql:variable("@searchVariable")]') AS The(ServerNode);

答案 2 :(得分:1)

另一种选择。这并没有使用XML.exist,但直接查找具有<source>元素和查找变量的User元素。然后将路径反转回祖先User并选择name属性。

DECLARE @xml XML=
'<User name="First Node" uniqueId="1332">
    <Task type="Form">
        <properties>
            <form>
                <formElement>
                    <populateValues>
                        <source>lookup</source>
                    </populateValues>
                </formElement>
            </form>
        </properties>
    </Task>
</User>';

DECLARE @lookup NVARCHAR(128)='lookup';

SELECT
    n.v.value('../../../../../../@name','NVARCHAR(128)') AS name
FROM
    @xml.nodes('//User/Task/properties/form/formElement/populateValues/source[.=sql:variable("@lookup")]') AS n(v);

显然不支持XPath轴(至少在SQL Server 2012上不支持)。如果它不是../../../../../../@name,那么您可以写出更简单的ancestor::User/@name