Update XML stored in a XML column in SQL Server Update - sql-server

Set Url value by applying filter based on location-type value from given sample.
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>

The XML you've shared is invalid so I've taken the liberty to make it valid in an effort to show you how I might approach this.
I've made the assumption you're looking to update values in a table somewhere in your environment. You can run this example in SSMS.
THIS IS MEANT AS AN EXAMPLE ONLY. DO NOT BLINDLY RUN THIS AGAINST PRODUCTION DATA.
First, I created a mock-up table that holds some page XML data. I assumed that a single XML row could contain multiple page nodes with duplicate location_type values.
DECLARE #Pages TABLE ( page_xml XML );
INSERT INTO #Pages ( page_xml ) VALUES (
'<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>' );
Selecting the current results from #Pages shows the following XML:
<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>
There are two pages with a location_type of 3, and one with a location_type of 8.
Next, I declared a few variables which I then used to modify the XML.
DECLARE #type INT = 3, #url VARCHAR(255) = 'http://www.google.com';
/* Update each Url text for the specified location_type */
WHILE EXISTS ( SELECT * FROM #Pages WHERE page_xml.exist( '//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url[text()!=sql:variable("#url")]' ) = 1 )
UPDATE #Pages
SET
page_xml.modify( '
replace value of (//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url/text()[.!=sql:variable("#url")])[1]
with sql:variable("#url")
' );
After running the update the XML now contains:
<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>http://www.google.com</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>http://www.google.com</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>
Using the WHILE EXISTS (... ensures that all Url nodes for the requested location_type are updated. In my example here there are two pages with a value of 3 that are updated, while location_type 8 is left alone.
Basically, what this is doing is updating the Url for any page with the requested location_type to the new #url value.
There are two key pieces here, the first being:
.exist( '//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url[text()!=sql:variable("#url")]' )
Which looks for the requested location_type that doesn't have the new #url value.
And the second:
page_xml.modify( '
replace value of (//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url/text()[.!=sql:variable("#url")])[1]
with sql:variable("#url")
' );
Which modifies (updates) the Url for the location type that has yet to be updated. The two "conditions" allow for a loop (WHILE) that ends when they're no longer met, ensuring that all page nodes for the requested location_type are updated.

Related

How to update empty XML node in MSSQL

I want to update empty XML variable in MSSQL
i try every solutions like replace, outer apply , modify but nothing working
please help me find a solutions
<ArrayOfField xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Field>
<FieldType>1</FieldType>
<DataType>1</DataType>
<Label>department</Label>
<Name>department</Name>
<Description />
<Min />
<Max />
<DefaultValue />
<Required>0</Required>
<SelectItems />
<Formula />
<DataSourceUID xsi:nil="true" />
<TrueText />
<FalseText />
<UnconfirmtyRule xsi:nil="true" />
<UnconfirmtyRequired xsi:nil="true" />
<UnconfirmityFormUID xsi:nil="true" />
<ConfirmationStatus xsi:nil="true" />
<ConfirmationUserBasedType xsi:nil="true" />
<ConfirmationRightUsers />
<Newline>false</Newline>
<ColumnWith>12</ColumnWith>
<Offset>0</Offset>
</Field>
<Field>
<FieldType>1</FieldType>
<DataType>4</DataType>
<Label>Test</Label>
<Name>test</Name>
<Description />
<Min />
<Max />
<DefaultValue />
<Required>0</Required>
<SelectItems />
<Formula />
<TrueText />
<FalseText />
<UnconfirmtyRule xsi:nil="true" />
<UnconfirmtyRequired xsi:nil="true" />
<UnconfirmityFormUID xsi:nil="true" />
<ConfirmationStatus xsi:nil="true" />
<ConfirmationUserBasedType xsi:nil="true" />
<ConfirmationRightUsers />
<Newline>false</Newline>
<ColumnWith>12</ColumnWith>
<Offset>0</Offset>
</Field>
</ArrayOfField>
I want update DefaultValue if DataType = 1
New DefaultValue like <DefaultValue>TODAY()<DefaultValue/>
I try this code but not update anything what is my mistake?
DECLARE #SearchType NVARCHAR(100)=N'1';
DECLARE #ReplaceWith NVARCHAR(100)=N'TODAY()';
UPDATE FormSchema
SET Fields.modify('replace value of
(/ArrayOfField
/Field[DataType=sql:variable("#SearchType")]
/DefaultValue/text())[1]
with sql:variable("#ReplaceWith")')
WHERE Fields.exist('/ArrayOfField
/Field[DataType=sql:variable("#SearchType")]')=1;
You need to use different XML modify operations depending on whether the target element is empty or not (i.e.: it contains no text as opposed to being xsi:nil="true"). For example...
--
-- Setup data...
--
create table dbo.FormSchema (
Fields xml
);
insert dbo.FormSchema (Fields) values
(N'<ArrayOfField>
<Field>
<DataType>1</DataType>
<DefaultValue>Hello, world!</DefaultValue>
</Field>
<Field>
<DataType>2</DataType>
<DefaultValue />
</Field>
</ArrayOfField>'),
(N'<ArrayOfField>
<Field>
<DataType>1</DataType>
<DefaultValue />
</Field>
<Field>
<DataType>3</DataType>
<DefaultValue />
</Field>
</ArrayOfField>');
--
-- Perform updates...
--
DECLARE
#SearchType NVARCHAR(100) = N'1',
#ReplaceWith NVARCHAR(100) = N'TODAY()';
UPDATE dbo.FormSchema
SET Fields.modify('
replace value of (/ArrayOfField/Field[DataType=sql:variable("#SearchType")]/DefaultValue/text())[1]
with sql:variable("#ReplaceWith")
')
WHERE Fields.exist('(/ArrayOfField/Field[DataType=sql:variable("#SearchType")]/DefaultValue/text())[1]')=1;
UPDATE dbo.FormSchema
SET Fields.modify('
insert text{sql:variable("#ReplaceWith")}
into (/ArrayOfField/Field[DataType=sql:variable("#SearchType")]/DefaultValue)[1]
')
WHERE Fields.exist('(/ArrayOfField/Field[DataType=sql:variable("#SearchType")]/DefaultValue)[1]')=1
AND Fields.exist('(/ArrayOfField/Field[DataType=sql:variable("#SearchType")]/DefaultValue/text())[1]')=0;
--
-- Check results...
--
SELECT * FROM dbo.FormSchema;
Which yields the results:
Fields
<ArrayOfField><Field><DataType>1</DataType><DefaultValue>TODAY()</DefaultValue></Field><Field><DataType>2</DataType><DefaultValue /></Field></ArrayOfField>
<ArrayOfField><Field><DataType>1</DataType><DefaultValue>TODAY()</DefaultValue></Field><Field><DataType>3</DataType><DefaultValue /></Field></ArrayOfField>

How can I find and update a node attribute value in an XML document based on match of a different attribute value in the same node?

Given my T-SQL stored procedure with:
Declare #SearchID varchar(35)
Set #SearchID = '5280301.2019050148902.00023'
Declare #DocListXML XML
Contains the following XML data
<JobList ListItems="7">
<Job JobFriendlyName="EMAIL INVOICES">
<DocumentList>
<Document Doc="1" ID="5280301.2019050148902.00020" Date="05-03-2019" Status="NEW" />
<Document Doc="2" ID="5280301.2019050148902.00022" Date="05-03-2019" Status="NEW" />
<Document Doc="3" ID="5280301.2019050148902.00023" Date="05-03-2019" Status="NEW" />
<Document Doc="4" ID="5280301.2019050104301.00055" Date="05-02-2019" Status="NEW" />
<Document Doc="5" ID="5280301.2019050104301.00056" Date="05-02-2019" Status="NEW" />
</DocumentList>
</Job>
<Job JobFriendlyName="INVOICES">
<DocumentList>
<Document Doc="6" ID="5280300.2019050148901.00001" Date="05-03-2019" Status="NEW" />
<Document Doc="7" ID="5280300.2019050148901.00002" Date="05-03-2019" Status="NEW" />
</DocumentList>
</Job>
</JobList>
I need T-SQL XML code to update this document and replace existing Status value with "OLD" for the node having ID attribute value matching the value of T-SQL local variable #SearchID.
After the update, the resulting document in #DocListXML should contain:
<JobList ListItems="7">
<Job JobFriendlyName="EMAIL INVOICES">
<DocumentList>
<Document Doc="1" ID="5280301.2019050148902.00020" Date="05-03-2019" Status="NEW" />
<Document Doc="2" ID="5280301.2019050148902.00022" Date="05-03-2019" Status="NEW" />
<Document Doc="3" ID="5280301.2019050148902.00023" Date="05-03-2019" Status="OLD" />
<Document Doc="4" ID="5280301.2019050104301.00055" Date="05-02-2019" Status="NEW" />
<Document Doc="5" ID="5280301.2019050104301.00056" Date="05-02-2019" Status="NEW" />
</DocumentList>
</Job>
<Job JobFriendlyName="INVOICES">
<DocumentList>
<Document Doc="6" ID="5280300.2019050148901.00001" Date="05-03-2019" Status="NEW" />
<Document Doc="7" ID="5280300.2019050148901.00002" Date="05-03-2019" Status="NEW" />
</DocumentList>
</Job>
</JobList>
I have found the modify/replace code to modify an attribute value of "X" "Y", but cannot find code for modify/update updating attribute [a] value in node with attribute [b] value matching a local variable.
Any help or suggestions would be appreciated.
Okay, after your last question you decided to got the CURSOR route, correct? ;-)
Try it like this.
--Your mockup up
Declare #SearchID varchar(35) = '5280301.2019050148902.00023';
Declare #DocListXML XML=
N'<JobList ListItems="7">
<Job JobFriendlyName="EMAIL INVOICES">
<DocumentList>
<Document Doc="1" ID="5280301.2019050148902.00020" Date="05-03-2019" Status="NEW" />
<Document Doc="2" ID="5280301.2019050148902.00022" Date="05-03-2019" Status="NEW" />
<Document Doc="3" ID="5280301.2019050148902.00023" Date="05-03-2019" Status="NEW" />
<Document Doc="4" ID="5280301.2019050104301.00055" Date="05-02-2019" Status="NEW" />
<Document Doc="5" ID="5280301.2019050104301.00056" Date="05-02-2019" Status="NEW" />
</DocumentList>
</Job>
<Job JobFriendlyName="INVOICES">
<DocumentList>
<Document Doc="6" ID="5280300.2019050148901.00001" Date="05-03-2019" Status="NEW" />
<Document Doc="7" ID="5280300.2019050148901.00002" Date="05-03-2019" Status="NEW" />
</DocumentList>
</Job>
</JobList>';
--This is the modifying command
SET #DocListXML.modify(N'replace value of (/JobList
/Job
/DocumentList
/Document[#ID=sql:variable("#SearchID")]
/#Status)[1] with "OLD"');
--check the result
SELECT #DocListXML;
Hint 1: This works only in cases where there is just one document with the given ID per XML!
Hint 2: You might shorten the command to this:
SET #DocListXML.modify(N'replace value of (//Document[#ID=sql:variable("#SearchID")]/#Status)[1] with "OLD"');
The deep search (triggered by the double slash // at the beginning of the XPath) tells the engine to find a <Document> fullfilling the predicate anywhere in your XML.

Algolia InstantSearch default Parameters / Scope

I have a React app that is using the Algoia InstantSearch library. This one https://community.algolia.com/react-instantsearch/
<InstantSearch
appId="KJH78673IUH"
apiKey="e55e048w7fh8wh8wrgh834c3ea51e3"
indexName="Events"
>
<Configure hitsPerPage={10} />
<SearchBox />
<h3>Latest Events</h3>
<Hits />
<Index indexName="Venues">
<h3>Latest Venues</h3>
<Hits />
</Index>
<Index indexName="Users">
<h3>Latest Users</h3>
<Hits />
</Index>
</InstantSearch>
This works fine and pulls the results as I type. However. On each of the Event, Venue and User records I have an attribute site_id. I am struggle to figure out how I can set the site_id to be like a default scope on the search query. Along the lines of how default_scope works in Rails. I am sure this is possible but failing to figure it out.
Any help would be appreciated!
I figured out how to do this using VirtualWidgets.
https://community.algolia.com/react-instantsearch/connectors/connectMenu.html
The scope an index to a particular attribute I used the following method:
import { connectMenu } from 'react-instantsearch/connectors';
const VirtualMenu = connectMenu(() => null);
// RESET OF COMPONENT CODE
<InstantSearch
appId="KJH78673IUH"
apiKey="e55e048w7fh8wh8wrgh834c3ea51e3"
indexName="Events"
/>
<Configure hitsPerPage={10} />
<VirtualMenu attribute="site_id" defaultRefinement="1" />
<SearchBox />
<h3>Latest Events</h3>
<Hits />
<Index indexName="Venues">
<h3>Latest Venues</h3>
<Hits />
</Index>
<Index indexName="Users">
<h3>Latest Users</h3>
<Hits />
</Index>
</InstantSearch>
You can also add the VirtualWidget to the Index block itself too.

Updating multiple XML columns using single update in SQL Server

I would like to update a table_A with new values for (AcresDist1, txtAcresDist1, txtAcresDist1Total) XML columns from a second table_B.
The column P_XML is of XML type. I know how to update a single column at once, but I would like to know how to update multiple columns in the XML using a single update statement. Thanks
SQL code:
UPDATE S
SET
P_XML.modify( N'replace value of (/FormValue/f1152_F1/Field[(id/text())[1]="AcresDist1"]/value/text())[1] with sql:column(''T.AcresDist1'')' )
, P_XML.modify( N'replace value of (/FormValue/f1152_F1/Field[(id/text())[1]="txtAcresDist1"]/value/text())[1] with sql:column(''T.txtAcresDist'')' )
, P_XML.modify( N'replace value of (/FormValue/f1152_F1/Field[(id/text())[1]="txtAcresDist1Total"]/value/text())[1] with sql:column(''T.txtAcresDist1Total'')' )
FROM
Table_A AS S
INNER JOIN
Table_B AS T ON s.P_NO = t.P_Number
AND s.FAC_RID = t.Fac_RID;
Here is the sample xml as requested. Thank you.
<FormValue>
<f1152>
<field>
<id>f1152_MainForm</id>
<value />
<tag />
<visible>true</visible>
<history>|09/28/2017 10:50:26 AM||</history>
<description />
<comment />
</field>
<field>
<id>txt_rdoCoverage</id>
<value>Development</value>
<tag />
<visible>false</visible>
<history>|09/28/2017 10:50:26 AM||</history>
<description />
<comment />
</field>
</f1152>
<f1152_F1>
<field>
<id>txtAcresDist1</id>
<value>1.2</value>
<tag />
<visible>false</visible>
<history>|09/28/2017 3:08:14 AM||</history>
<description />
<comment />
</field>
<field>
<id>txtAcresDist1Total</id>
<value>200</value>
<tag />
<visible>false</visible>
<history>|09/28/2017 3:08:14 AM||</history>
<description />
<comment />
</field>
</f1152_F1>

sql query a varchar(max) column to select an element based on the value of one of its children children's

I am trying to search xml data stored in a varchar(max) column.
Below is an example of one of the xml data strings found in the varchar column
With this example I will have sql variable called #dsName which is to be matched against the node when its values matches #dsName and it is subordinate to either or nodes. The nodes in between or until you get to can vary.
<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>
Here is an example of the select I used when trying to load into a variable of an XML data type. The varchar(max) column name is XML
DECLARE #xml XML=
(SELECT [XML]
FROM ProcessModels
WHERE [XML] LIKE '%<datasourceName>' + #dsName + '%'
and [status] = 'ACTIVE')
The SQL select is that same if I load it into a table variable and the #dsName variable is already set with the string to search for.
In this example I want to find name of every Server node and/or User node when it has a node with the value of 'get datasource list'. The string 'get datasource list' already exists in the variable #dsName.
The following query gets me half way there.
select sn.value('#name', 'varchar(100)') AS ServerNodes
from #xml.nodes('/business_process/ProcessDefinition/Server') AS ServerNodes(sn)
Now I need to figure out how to limit the Server.#name returned to just those where the child node //datasourceName value equals the sql:variable.
This worked:
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
As my first answer is already very crowded...
With this you'd get the User's and the Server's data out of the XML. If you set the #searchVariable to a non existent datasourceName-value, the User data is still there, but the Server data will be NULL. Try it out!
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);
Your friend is sql:variable(), there is also a sql:column() if your search value comes from a table's column.
According to your comment I mock up one table with an XML column of type varchar. The SELECT will first use CROSS APPLY to cast this to "real" XML, then .exist() is used to pick up the rows fullfilling your criteria and finally the value of /User/#name is returned.
If you change the lookup-variable to "another" you'll find the other XML, other strings will come back empty.
As examples of XPath I give you three queries all returning the same. This depends on your 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
Yet another alternative. This doesn't use XML.exist but looks straight for User elements having a <source> element with the lookup variable. Then the path is reversed back to ancestor User and the name attribute is selected.
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);
Apparantly XPath axes aren't fully supported (at least not on SQL Server 2012). If it were instead of ../../../../../../#name you could have written the easier ancestor::User/#name.

Resources