Parse an XML value with a colon in SQL Server - sql-server

I need help to parse XML in SQL Server. I need to get "d1p1:Val2" value and concatenation of values for "d2p1:string".
<FirstData xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d1p1="http://XXXXXX" xmlns="http://YYYYYY" i:type="d1p1:StaticInfo">
<Timestamp>0</Timestamp>
<ActionResult i:nil="true" />
<d1p1:Val1 xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:string>1</d2p1:string>
<d2p1:string>2</d2p1:string>
<d2p1:string>3</d2p1:string>
<d2p1:string>4</d2p1:string>
</d1p1:Val1>
<d1p1:Val2>false</d1p1:Val2>
</FirstData>

Your question is not very clear, but this might help you:
DECLARE #xml XML=
N'<FirstData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d1p1="http://XXXXXX" xmlns="http://YYYYYY" i:type="d1p1:StaticInfo">
<Timestamp>0</Timestamp>
<ActionResult i:nil="true" />
<d1p1:Val1 xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:string>1</d2p1:string>
<d2p1:string>2</d2p1:string>
<d2p1:string>3</d2p1:string>
<d2p1:string>4</d2p1:string>
</d1p1:Val1>
<d1p1:Val2>false</d1p1:Val2>
</FirstData>';
WITH XMLNAMESPACES(DEFAULT 'http://YYYYYY'
,'http://XXXXXX' AS d1p1
,'http://schemas.microsoft.com/2003/10/Serialization/Arrays' AS d2p1)
SELECT #xml.value(N'(/FirstData/d1p1:Val2/text())[1]','bit') AS D1P1_Val2
,#xml.query(N'data(/FirstData/d1p1:Val1/d2p1:string/text())').value(N'text()[1]',N'nvarchar(max)') AS AllStrings;
The result
D1P1_Val2 AllStrings
0 1 2 3 4
This is the - not recommended - minimal query:
SELECT #xml.value(N'(//*:Val2)[1]','bit') AS D1P1_Val2
,#xml.query(N'data(//*:string)').value(N'.',N'nvarchar(max)') AS AllStrings;

Try something like this (can't test it myself ) :
SELECT Instructions.query('
declare namespace d1p1="http://XXXXXX";
declare namespace d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays";
concat(//d1p1:Val2, " ", //d2p1:string[1]);
')
I think you just have to elaborate a bit on this

Related

SQL Server - XQuery: delete parent node based on child value substring

I want to delete all parent nodes TxDtls of the following XML where position 20 of child value Ref is 2.
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04 camt.054.001.04.xsd">
<BkToCstmrDbtCdtNtfctn>
<Ntfctn>
<Ntry>
<TtlChrgsAndTaxAmt Ccy="CHF">1.60</TtlChrgsAndTaxAmt>
<Rcrd>
<Amt Ccy="CHF">1.60</Amt>
<CdtDbtInd>DBIT</CdtDbtInd>
<ChrgInclInd>false</ChrgInclInd>
<Tp>
<Prtry>
<Id>2</Id>
</Prtry>
</Tp>
</Rcrd>
</Chrgs>
<NtryDtls>
<TxDtls>
<RmtInf>
<Strd>
<CdtrRefInf>
<Ref>111118144400000000020076766</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</TxDtls>
<TxDtls>
<RmtInf>
<Strd>
<CdtrRefInf>
<Ref>111117645600000000030076281</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</TxDtls>
</NtryDtls>
</Ntry>
</Ntfctn>
</BkToCstmrDbtCdtNtfctn>
</Document>
So I want to delete the first TxDtls node (substring position 20 = 2) while I want to keep the second one (substring position 20 <> 2).
I tried this:
UPDATE mytable SET XMLData.modify('delete .//TxDtls[RmtInf/Strd/CdtrRefInf/Ref/substring(text(),20,1) = ''2'']')
However, I get the error "The XQuery syntax '/function()' is not supported". Any hints on how to achieve this?
Thanks
What a difference made by the partially provided minimal reproducible example.
The XML is still not well-formed. I had to comment out the following tag: </Chrgs>
A default namespace is easily handled by its declaration in the XQuery method.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO #tbl (xmldata) VALUES
(N'<?xml version="1.0"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04 camt.054.001.04.xsd">
<BkToCstmrDbtCdtNtfctn>
<Ntfctn>
<Ntry>
<TtlChrgsAndTaxAmt Ccy="CHF">1.60</TtlChrgsAndTaxAmt>
<Rcrd>
<Amt Ccy="CHF">1.60</Amt>
<CdtDbtInd>DBIT</CdtDbtInd>
<ChrgInclInd>false</ChrgInclInd>
<Tp>
<Prtry>
<Id>2</Id>
</Prtry>
</Tp>
</Rcrd>
<!--</Chrgs>-->
<NtryDtls>
<TxDtls>
<RmtInf>
<Strd>
<CdtrRefInf>
<Ref>111118144400000000020076766</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</TxDtls>
<TxDtls>
<RmtInf>
<Strd>
<CdtrRefInf>
<Ref>111117645600000000030076281</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</TxDtls>
</NtryDtls>
</Ntry>
</Ntfctn>
</BkToCstmrDbtCdtNtfctn>
</Document>');
-- DDL and sample data population, end
-- before
SELECT * FROM #tbl;
UPDATE #tbl SET xmldata.modify('declare default element namespace "urn:iso:std:iso:20022:tech:xsd:camt.054.001.04";
delete /Document/BkToCstmrDbtCdtNtfctn/Ntfctn/Ntry/NtryDtls/TxDtls[RmtInf/Strd/CdtrRefInf/Ref[substring(./text()[1],20,1) = "2"]]');
-- after
SELECT * FROM #tbl;

what is wrong with my simple sqlxml query?

Sorry,
Been struggle with this query for a while
declare #a xml
select #a='<GrsAutoCompleteCodes>
<GrsAutoCompleteCode CitiCode="00DX" IsMatched="0" HasCustomName="0" />
<GrsAutoCompleteCode CitiCode="00G0" IsMatched="0" HasCustomName="0" />
</GrsAutoCompleteCodes>'
SELECT
p.s.value('(CitiCode/text())[1]','VARCHAR(100)') AS CitiCode
FROM #a.nodes('/GrsAutoCompleteCode') p(s)
This query somehow returns no records, what am I doing wrongly?
Because it's completely wrong, I'm afraid. Your root is GrsAutoCompleteCodes not GrsAutoCompleteCode, and CitiCode isn't a node it's a property of GrsAutoCompleteCode.
Perhaps you want this, which returns the value of CitiCode for the first GrsAutoCompleteCode node:
DECLARE #a xml;
SET #a = '<GrsAutoCompleteCodes>
<GrsAutoCompleteCode CitiCode="00DX" IsMatched="0" HasCustomName="0" />
<GrsAutoCompleteCode CitiCode="00G0" IsMatched="0" HasCustomName="0" />
</GrsAutoCompleteCodes>'
SELECT a.GACC.value('(GrsAutoCompleteCode/#CitiCode)[1]','varchar(4)') AS CitiCode
FROM #a.nodes('/GrsAutoCompleteCodes')a(GACC);
If you want the value of every CitiCode, that would be this:
SELECT a.GACC.value('#CitiCode','varchar(4)') AS CitiCode
FROM #a.nodes('/GrsAutoCompleteCodes/GrsAutoCompleteCode')a(GACC);

extract soap xml tag value containing multiple namespaces and different attribute in SQL Sever

Below is the sample SOAP xml and query to extract the value -
DECLARE #xml XML='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<xsi:MaintenanceOrder xmlns:xsi="http://schema.xyz.com/abc/2" xmlns:ush="http://www.xyz.nl/abc" xmlns:xsj="http://www.w3.org/2001/XMLSchema-instance" languageCode="en-US" releaseID="9.2" systemEnvironmentCode="Production" versionID="2.8.0">
<xsi:DataArea>
<xsi:MaintenanceOrder>
<xsi:MaintenanceOrderHeader>
<xsi:UserArea>
<xsi:Property>
<xsi:NameValue accountingEntity="*" listID="*" name="OrderDate" type="DATE">2020-03-30T00:00:00</xsi:NameValue>
</xsi:Property>
<xsi:Property>
<xsi:NameValue accountingEntity="*" listID="*" name="ReportDate" type="DATE">2020-04-30T00:00:00</xsi:NameValue>
</xsi:Property>
</xsi:UserArea>
</xsi:MaintenanceOrderHeader>
</xsi:MaintenanceOrder>
</xsi:DataArea>
</xsi:MaintenanceOrder>
</soapenv:Body>
</soapenv:Envelope>'
select A.r.value('(xsi:NameValue[#name="OrderDate"])[1]','date') as "OrderDate"
,A.r.value('(xsi:NameValue[#name="ReportDate"])[1]','date') as "ReportDate"
FROM #xml.nodes('/*:Envelope/*:Body/*:MaintenanceOrder/*:DataArea/*:MaintenanceOrder/*:MaintenanceOrderHeader/*:UserArea/*:Property') AS A(r)
issue 1 - by removing namespace in MaintenanceOrder only then query returns value otherwise returns null.
issue 2 - the required output is single line with multiple tags value, but the query is giving multiple rows.
Any help would be highly appreciated.
Try to use with xmlnamespaces declarations and explicit paths so that your XPath queries match only the elements you expect - and perform as well as can be expected.
For example:
declare #xml xml =
N'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<xsi:MaintenanceOrder xmlns:xsi="http://schema.xyz.com/abc/2" xmlns:ush="http://www.xyz.nl/abc" xmlns:xsj="http://www.w3.org/2001/XMLSchema-instance" languageCode="en-US" releaseID="9.2" systemEnvironmentCode="Production" versionID="2.8.0">
<xsi:DataArea>
<xsi:MaintenanceOrder>
<xsi:MaintenanceOrderHeader>
<xsi:UserArea>
<xsi:Property>
<xsi:NameValue accountingEntity="*" listID="*" name="OrderDate" type="DATE">2020-03-30T00:00:00</xsi:NameValue>
</xsi:Property>
<xsi:Property>
<xsi:NameValue accountingEntity="*" listID="*" name="ReportDate" type="DATE">2020-04-30T00:00:00</xsi:NameValue>
</xsi:Property>
</xsi:UserArea>
</xsi:MaintenanceOrderHeader>
</xsi:MaintenanceOrder>
</xsi:DataArea>
</xsi:MaintenanceOrder>
</soapenv:Body>
</soapenv:Envelope>';
with xmlnamespaces (
'http://schemas.xmlsoap.org/soap/envelope/' as s11,
'http://schema.xyz.com/abc/2' as abc
)
select
A.r.value('(abc:Property/abc:NameValue[#name="OrderDate"])[1]','date') as "OrderDate",
A.r.value('(abc:Property/abc:NameValue[#name="ReportDate"])[1]','date') as "ReportDate"
from #xml.nodes('/s11:Envelope/s11:Body/abc:MaintenanceOrder/abc:DataArea/abc:MaintenanceOrder/abc:MaintenanceOrderHeader/abc:UserArea') as A(r);
Returns the expected values in a single row:
OrderDate ReportDate
2020-03-30 2020-04-30

SQL replace value of with always random xml code

I have the following SQL XML in several rows of a table (table is tbldatafeed column in configuration_xml). All of the UserName="" and Password="" is different each time for each row and does not repeat so I can not find/replace off of that. I am trying to write a query that finds all of those and replaces them with Username/Passwords I choose.
<DataFeed xmlns="http://www.tech.com/datafeed/dfx/2010/04" xmlns:plugin="pluginExtensions" Type="TODO" Guid="TODO" UserAccount="DF_LEAN_PopulateCommentsSubForm" Locale="en-US" DateFormat="" ThousandSeparator="" NegativeSymbol="" DecimalSymbol="" SendingNotifications="false" SendJobStatusNotifications="false" RecipientUserIds="" RecipientGroupIds="" RecipientEmailAddresses="" Name="CI_C11.01_Lean-Lean_Reject Comments_A2A" >
<Transporter>
<transporters:ArcherWebServiceTransportActivity xmlns:transporters="clr-namespace:ArcherTech.DataFeed.Activities.Transporters;assembly=ArcherTech.DataFeed" xmlns:out="clr-namespace:ArcherTech.DataFeed;assembly=ArcherTech.DataFeed" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:compModel="clr-namespace:ArcherTech.DataFeed.ComponentModel;assembly=ArcherTech.DataFeed" xmlns:channel="clr-namespace:ArcherTech.DataFeed.Engine.Channel;assembly=ArcherTech.DataFeed" xmlns:engine="clr-namespace:ArcherTech.DataFeed.Engine;assembly=ArcherTech.DataFeed" xmlns:kernel="clr-namespace:ArcherTech.Kernel.Channel;assembly=ArcherTech.Kernel" xmlns="clr-namespace:ArcherTech.DataFeed;assembly=ArcherTech.DataFeed" xmlns:schema="clr-namespace:System.Xml.Schema;assembly=System.Xml" xmlns:xmlLinq="clr-namespace:System.Xml.Linq;assembly=System.Xml" xmlns:domain="clr-namespace:ArcherTech.Common.Domain;assembly=ArcherTech.Common" xmlns:s="clr-namespace:System;assembly=mscorlib" x:Key="transportActivity" SearchType="ReportId" Uri="https://arcs-d" RecordsPerFile="100" ReportID="EC514865-88D5-49CE-A200-7769EC1C2A88" UseWindowsAuth="false" IsWindowsAuthSpecific="false" WindowsAuthUserName="i9XzCczAQ7J2rHwkg6wG9QF8+O9NCYJZP6y5Kzw4be0+cdvUaGu/9+rHuLstU736pnQrRcwmnSIhd6oPKIvnLA==" WindowsAuthPassword="+y0tCAKysxEMSGv1unpHxfg6WjH5XWylgP45P5MLRdQ6+zAdOLSVy7s3KJa3+9j2i83qn8I8K7+1+QBlCJT1E7sLQHWRFOCEdJgXaIr1gWfUEO+7kjuJnZcIEKZJa2wHyqc2Z08J2SKfdCLh7HoLtg==" WindowsAuthDomain="" ProxyName="" ProxyPort="8080" ProxyUsername="" ProxyPassword="" ProxyDomain="" IsProxyActive="False" ProxyOption="None" InstanceName="ARCS-D" TempFileOnSuccessAction="DoNothing" TempFileOnSuccessRenameString="" TempFileOnErrorAction="DoNothing" TempFileOnErrorRenameString="" Transform="{engine:DataFeedBinding Path=Transform}" SessionContext="{engine:DataFeedBinding Path=Session}">
<transporters:ArcherWebServiceTransportActivity.Credentials>
<NetworkCredentialWrapper UserName="TeSZmI1SqO0eJ0G2nDVU+glFg/9eZfeMppYQnPfbeg8=" Password="Slt4VHqjkYscWyCwZK40QJ7KOQroG9OTKr+RGt9bQjE=" />
</transporters:ArcherWebServiceTransportActivity.Credentials>
</transporters:ArcherWebServiceTransportActivity>
</Transporter>
</DataFeed>
I need to be able to set a value and replace it with a query
I have written the following
select #config_xml=configuration_xml from bldatafeed where datafeed_name = 'REMOVED'
update tbldatafeed set configuration_xml.modify(//*:NetworkCredentialWrapper/#UserName)[1] with "abc" ')
where datafeed_name = 'REMOVED'
This does the trick but it only works if I set the "abc" password each time in each area and in some cases I am running this against 50+ rows.
I also tried:
Declare #server nvarchar(max) = 'abc'
Declare #config_xml xml
select #config_xml=configuration_xml from bldatafeed where datafeed_name = 'REMOVED'
update tbldatafeed set configuration_xml.modify(//*:NetworkCredentialWrapper/#UserName)[1] with #server ')
where datafeed_name = 'REMOVED'
The error from this is that: XQuery [tbldatafeed.configuration_xml.modify()]: Top-level attribute nodes are not supported
What I would like to be able to do is set my variable and utilize that as I will be setting this up for multiple rows and unfortunately this error is making this a very difficult problem to solve.
Thanks for any help, this has kept me confused for a bit.
Use the function sql:variable() to use a variable in the XQuery expression.
declare #T table(X xml);
insert into #T values('<X UserName=""/>');
declare #UserName nvarchar(max) = 'abc'
update #T set
X.modify('replace value of (/X/#UserName)[1]
with sql:variable("#UserName")');

Parse simple XML in SQL Server

How to parse the following xml as recordset?
<root>
<240>0</240>
<241>1</241>
<242>2</242>
<243>3</243>
<249>4</249>
</root>
<root 240="0" 241="1" 242="2" 243="3" 249="4"/>
When I try
declare #ids xml = N'<root><240>0</240><241>1</241></root>'
SELECT T.Item.value('240[1]', 'int')
from #ids.nodes('/root') AS T(Item)
I get an error
ML parsing: line 1, character 8, illegal qualified name character: declare #ids xml = N'<240>0' SELECT T.Item.value('a[1]', 'int') from #ids.nodes('/root') AS T(Item)
But generally I need the following output:
|240|0|
|241|1|
...
When xml elements are named as usual everything is Ok (<row key=240 value="0"/>).
XML disallows to use numbers as first character of an element name. Use format from your example:
<row key=240 value="0"/>

Resources