Please check below query.
declare #xmlRoot as xml
set #xmlRoot= '<Root>
<table1 col1="2012-03-02T16:42:55.777">
<table2Array>
<Table2 col2="abc">
</Table2>
<Table2 col2="def">
</Table2>
</table2Array>
</table1>
<table1 col1="2012-03-02T17:42:55.777">
<table2Array>
<Table2 col2="abc1">
</Table2>
<Table2 col2="def1">
</Table2>
</table2Array>
</table1>
</Root>'
declare #a as varchar(1)
set #a= '1'
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[1]/table2Array/Table2' ) AS T(item);
--The above query return expected output
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[*[local-name()=sql:variable("#a")]]/table2Array/Table2' )
AS T(item);
--The above query doesn't return expected output
what am I doing wrong here?
Since I dont have a key value in parent node to identify child node. I have to parse through index.
This worked for me:
DECLARE #a INT; -- data type is probably important!
SET #a = 1;
SELECT col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[sql:variable("#a")]/table2Array/Table2') AS T(item);
Related
I have a table with two xml columns and the data inside the table is large. I would like to filter on the column which is of xml type to check if it contains a ID.
My sample xml column IncomingXML and value looks like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sws="abc">
<soapenv:Header>
<To xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none" soapenv:mustUnderstand="1">abc</To>
<Action xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none" soapenv:mustUnderstand="1">student</Action>
</soapenv:Header>
<soapenv:Body>
<sws:ProvisionStudent>
<sws:provisionStudentInput>
<sws:Operation>U</sws:Operation>
<sws:ADAccount>SU123456789</sws:ADAccount>
<sws:ADPassword>abcde</sws:ADPassword>
<sws:Prefix />
<sws:FirstName>ancd</sws:FirstName>
<sws:LastName>xyz</sws:LastName>
<sws:MiddleName />
<sws:Suffix />
<sws:Email>abc#yahoo.com</sws:Email>
<sws:EmplId>123456789</sws:EmplId>
<sws:CampusCode />
<sws:CompletionYear>0</sws:CompletionYear>
<sws:CurrentCumulativeGpa>0</sws:CurrentCumulativeGpa>
<sws:GraduationGpa />
<sws:GraduationProgramCode />
<sws:ProgramCode />
<sws:UserType />
</sws:provisionStudentInput>
</sws:ProvisionStudent>
</soapenv:Body>
</soapenv:Envelope>
Please help me with a query like
select *
from table
where IncomingXML like '%SU123456789%'
I tried the following but did not have any luck.
select *
from table
where cast(IncomingXML as nvarchar(max)) like '%SU123456789%'
You can use XQuery for this
DECLARE #toSearch nvarchar(100) = 'SU123456789';
WITH XMLNAMESPACES(
'http://schemas.xmlsoap.org/soap/envelope/' AS soapenv,
DEFAULT 'abc'
)
SELECT *
FROM YourTable t
WHERE t.IncomingXML.exist('/soapenv:Envelope/soapenv:Body/ProvisionStudent/provisionStudentInput/ADAccount[text() = sql:variable("#toSearch")]') = 1;
To search in any node, at the cost of efficiency
DECLARE #toSearch nvarchar(100) = 'SU123456789';
SELECT *
FROM YourTable t
WHERE t.IncomingXML.exist('//*[text() = sql:variable("#toSearch")]') = 1;
db<>fiddle
You can obviously also embed a literal instead of a variable in the XQuery.
I have the below in an xml column of a table. How can I write a query that replaces only part of the value for all ? The text REPLACE will be replaced with another value.
<Root>
<Response xmlns:ns1="urn:names:tc:legalxml-message1:schema:xsd:Message-4.0">
<Message xmlns:ns2="urn:names:tc:legalxml-message2:schema:xsd:Types-4.0">
<Response1>
<ns1:Name>REPLACE name1</Name>
</Response1>
<Response2>
<ns1:Name>REPLACE name2</Name>
</Response2>
<Response3>
<ns1:Name>REPLACE name3</Name>
</Response3>
<Response4>
<ns1:Name>REPLACE name4</Name>
</Response4>
</Message>
</Response>
</Root>
I tried the below query and received an error message.
XQuery [r.x.modify()]: The target of 'replace value of' must be a non-metadata attribute or an element with simple typed content, found 'element(Name,xdt:untyped) ?'
I was following this link.
declare #SearchString varchar(100),#ReplaceString varchar(100)
SELECT #SearchString = 'REPLACE',#ReplaceString = 'NEWVALUE'
UPDATE r
SET x.modify('replace value of (/Root/Response/Message/Response1/Name)[1] with sql:column("y")')
FROM (SELECT xmlColumn,REPLACE(t.u.value('Name[1]','varchar(100)'),#SearchString,#ReplaceString) as y
FROM tblMessage
CROSS APPLY tblMessage.nodes('/Root/Response/Message/Response1/Name')t(u)
)r
Given the following sample data:
DECLARE #xml XML =
'<Root>
<Response xmlns:ns1="urn:names:tc:legalxml-message1:schema:xsd:Message-4.0">
<Message xmlns:ns2="urn:names:tc:legalxml-message2:schema:xsd:Types-4.0">
<Response1>
<ns1:Name>REPLACE name1</ns1:Name>
</Response1>
<Response2>
<ns1:Name>REPLACE name2</ns1:Name>
</Response2>
<Response3>
<ns1:Name>REPLACE name3</ns1:Name>
</Response3>
<Response4>
<ns1:Name>REPLACE name4</ns1:Name>
</Response4>
</Message>
</Response>
</Root>';
DECLARE #replaceText VARCHAR(100) = 'Something New Here';
You can use the XML modify method to update the values. To do so explicitly by node name you could do this:
SET #xml.modify('replace value of (/Root/Response/Message/Response4/*:Name/text())[1]
with sql:variable("#replaceText")');
In this ^^ example I'm updating the Response4 node. You can also use the node position to update the XML. The example below will update Response1:
SET #xml.modify('replace value of (/Root/Response/Message//*:Name/text())[1]
with sql:variable("#replaceText")');
To update Response2 you would do change the [1] to [2] like so:
SET #xml.modify('replace value of (/Root/Response/Message//*:Name/text())[2]
with sql:variable("#replaceText")');
I try to rename element <Visible> to <IsVisible>, but this SELECT returns element Visible without child elements, how can I get Visible with UserId and RoleId elements?
DECLARE #xml XML =
N'<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FieldId>2200</FieldId>
<Visible xsi:type="UserRole">
<UserId xsi:type="CurrentUserId" />
<RoleId>26</RoleId>
</Visible>
</Root>';
SELECT #xml.query(N'let $nd:=(//*[local-name()="Visible"])[1]
return
<IsVisible> {$nd/#*}
{$nd/text()}
</IsVisible>
')
Try this.
DECLARE #xml XML =
N'<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FieldId>2200</FieldId>
<Visible xsi:type="UserRole">
<UserId xsi:type="CurrentUserId" />
<RoleId>26</RoleId>
</Visible>
</Root>';
SELECT #xml.query(N'let $nd:=(//*[local-name()="Visible"])[1]
return
<IsVisible> {$nd/#*}
{$nd/*}
</IsVisible>
')
You can either use XQuery (FLWOR) or a simple replace
DECLARE #xml XML =
N'<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FieldId>2200</FieldId>
<Visible xsi:type="UserRole">
<UserId xsi:type="CurrentUserId" />
<RoleId>26</RoleId>
</Visible>
</Root>';
--Works, but will reorganise your namespace declarations
WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema' AS xsd
,'http://www.w3.org/2001/XMLSchema-instance' AS xsi)
SELECT #xml.query(N'<Root>
{
for $nd in /Root/*
return
if(local-name($nd)!="Visible") then
$nd
else
<IsVisible>{$nd/#*}
{$nd/*}
</IsVisible>
}
</Root>
');
--Might be easier here
SELECT CAST(
REPLACE(REPLACE(
CAST(#xml AS NVARCHAR(MAX))
,'<Visible ','<IsVisible ')
,'</Visible>','</IsVisible>')
AS XML)
I'm querying using OpenXML to retrieve the cap elements between the subject elements in XML I don't want the cap between the support elemements. The query works great to retrieve one value but fails when there are multiple element nodes.
<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>
CREATE Table #XmlTemp(XmlField Xml);
Set Nocount On;
Insert Into #XmlTemp(XmlField)
Select '<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>'As XmlField;
Declare #xmlData Xml;
Select #xmlData = XmlField From #XmlTemp;
Declare #document int;
Exec sp_xml_preparedocument #document Output, #xmlData, NULL;
SELECT ID,Cap FROM(
SELECT ID,Cap FROM OpenXml(#document,'./First/Test', 0) With (ID varchar(max)'./#id', Cap Varchar(max) './subject/cap')) alias
drop table #xmltemp
It'd be fairly time consuming to change the query to use .nodes method more so because of the testing involved so I'd like it to stay as OpenXML if possible.
I'd only like to retrieve out the ID and then the multiple cap element values.
Thank you for your time.
I can't see why the query using .nodes is complex. Just
SELECT t.n.value('(/First/Test/#id)[1]', 'int') id
, t.n.value('(.)[1]', 'int') cap
from #xmlData.nodes('./First/Test/subject/cap') t(n);
And OpenXML version
SELECT ID,Cap FROM(
SELECT ID,Cap
FROM OpenXml(#document,'./First/Test/subject/cap', 0)
With (ID varchar(max) '/First/Test/#id'
, Cap Varchar(max) '.')) alias
Version for the edited question
SELECT ID,Cap FROM(
SELECT ID,Cap
FROM OpenXml(#document,'/First/Test/subject/cap', 0)
With (ID varchar(max) '../../#id'
, Cap Varchar(max) '.')) alias
It returns only subject/cap and #id of the proper parent:
ID Cap
1 83847 15
2 83847 25
3 83847 100
4 83848 150
5 83848 2
6 83848 10
Your XML is double nested. You have 1:n of <Test> elements within <First> and again 1:n of <cap> elements within <subject>.
The proper way to query this is diving into the XML strictly forward:
CREATE Table #XmlTemp(XmlField Xml);
Set Nocount On;
Insert Into #XmlTemp(XmlField)
Select '<First>
<Test id="83847">
<subject>
<cap>15</cap>
<cap>25</cap>
<cap>100</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
<Test id="83848">
<subject>
<cap>150</cap>
<cap>2</cap>
<cap>10</cap>
</subject>
<support>
<cap>9</cap>
</support>
</Test>
</First>'As XmlField;
--The query will use .nodes() to get all <Test> elements and again .nodes() to get the related <cap> elements:
SELECT t.value('#id', 'int') id
,c.value('text()[1]', 'int') cap
from #XmlTemp AS tbl
CROSS APPLY tbl.XmlField.nodes('/First/Test') AS A(t)
CROSS APPLY A.t.nodes('subject/cap') AS B(c);
GO
DROP TABLE #XmlTemp;
What am I not getting here? I can't get any return except NULL...
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
DECLARE #nodeVal int
SELECT #nodeVal = #xml.value('(errorFlag)[1]', 'int')
SELECT #nodeVal
Here is the solution:
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
declare #table table (data xml);
insert into #table values (#xml);
WITH xmlnamespaces (
'http://schemas.xmlsoap.org/soap/envelope/' as [soap])
SELECT Data.value('(/soap:Envelope/soap:Body/webregdataResponse/errorFlag)[1]','int') AS ErrorFlag
FROM #Table ;
Running the above SQL will return 99.
Snapshot of the result is given below,
That's because errorFlag is not the root element of your XML document. You can either specify full path from root element to errorFlag, for example* :
SELECT #nodeVal = #xml.value('(/*/*/*/errorFlag)[1]', 'int')
or you can use descendant-or-self axis (//) to get element by name regardless of it's location in the XML document, for example :
SELECT #nodeVal = #xml.value('(//errorFlag)[1]', 'int')
*: I'm using * instead of actual element name just to simplify the expression. You can also use actual element names along with the namespaces, like demonstrated in the other answer.