Parse selected values from XML in SQL

I have the following XML file (I put here only a short scheme), it’s a SEPA XML bank statement. I’m not familiar with parsing XML files, my next move will be inserting and comparing to data stored in the SQL databases for error checks. Sadly, I know what to do next, don’t know how to make progress with my first step. All I need is to create a table to select values of 2 attributes from a file stored at a particular place

<?xml version="1.0" encoding="utf-8" standalone="yes"?> <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">   <BkToCstmrStmt>     <GrpHdr>       ..........     </GrpHdr>     <Stmt>       <Ntry>         <Amt Ccy="EUR">RequestedAmount1</Amt>         <AddtlNtryInf>RequestedInfo1</AddtlNtryInf>       </Ntry>       <Ntry>         <Amt Ccy="EUR">RequestedAmount2</Amt>         <AddtlNtryInf>RequestedInfo2</AddtlNtryInf>       </Ntry>       <Ntry>         <Amt Ccy="EUR">RequestedAmount3</Amt>         <AddtlNtryInf>RequestedInfo3</AddtlNtryInf>       </Ntry>     </Stmt>   </BkToCstmrStmt> </Document> 

If the XML structure was simpler, for example like this…

<?xml version="1.0" encoding="utf-8" standalone="yes"?> <Ntry>   <Amt Ccy="EUR">RequestedAmount1</Amt>   <AddtlNtryInf>RequestedInfo1</AddtlNtryInf> </Ntry> <Ntry>   <Amt Ccy="EUR">RequestedAmount2</Amt>   <AddtlNtryInf>RequestedInfo2</AddtlNtryInf> </Ntry> <Ntry>   <Amt Ccy="EUR">RequestedAmount3</Amt>   <AddtlNtryInf>RequestedInfo3</AddtlNtryInf> </Ntry> 

…then I’d use this query to select requested attributes Amt and AddtlNtryInf and it works perfectly

SELECT    MY_XML.Ntry.query('Amt').value('.', 'NVARCHAR(255)') AS Amt,    MY_XML.Ntry.query('AddtlNtryInf').value('.', 'NVARCHAR(255)') AS AddtlNtryInf FROM (SELECT CAST(MY_XML AS xml)       FROM OPENROWSET(BULK 'C:\tmp\TestSqlSimple.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)       CROSS APPLY MY_XML.nodes('Ntry') AS MY_XML (Ntry); 

But don’t know how to deal with that more complicated one. I’ve tried something like this and several similar attempts but I failed because it doesn’t select anything, the result is nothing

SELECT    MY_XML.Ntry.query('Amt').value('.', 'NVARCHAR(255)') AS Amt,    MY_XML.Ntry.query('AddtlNtryInf').value('.', 'NVARCHAR(255)') AS AddtlNtryInf FROM (SELECT CAST(MY_XML AS xml)       FROM OPENROWSET(BULK 'C:\tmp\TestSqlSimple.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)       CROSS APPLY MY_XML.nodes('/Document/BkToCstmrStmt/Stmt/Ntry') AS MY_XML (Ntry); 

Can’t figure out what to do with that CROSS APPLY. Thank you very much for any suggestions or improvements, you’re doing a great job

Add Comment
2 Answer(s)

You were almost there.

(1) The XML file has a default namespace, and it needs a special treatment via XMLNAMESPACES clause.

(2) The Amt element probably has a numeric value so you could use DECIMAL(x,y) data type. But I kept the NVARCHAR(255) to match the obfuscated XML file example.

(3) The SQL below is using .value() method without unnecessary .query() method.

(4) It is a good practice to use elementName/text() technique for performance reasons. It is MS SQL Server specific peculiarity.

SQL

-- DDL and sample data population, start DECLARE @tbl TABLE (    ID INT IDENTITY PRIMARY KEY,    Amt NVARCHAR(255),    AddtlNtryInf NVARCHAR(255) ); -- DDL and sample data population, end  ;WITH XMLNAMESPACES (DEFAULT 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02')      , XmlFile (xmlData) AS (    SELECT TRY_CAST(BulkColumn AS XML)     FROM OPENROWSET(BULK 'e:\Temp\TestSqlSimple.xml', CODEPAGE = '65001', SINGLE_BLOB) AS x ) INSERT INTO @tbl (Amt, AddtlNtryInf) SELECT c.value('(Amt/text())[1]', 'NVARCHAR(255)') AS Amt     , c.value('(AddtlNtryInf/text())[1]', 'NVARCHAR(255)') AS AddtlNtryInf FROM XmlFile CROSS APPLY xmlData.nodes('/Document/BkToCstmrStmt/Stmt/Ntry') AS t(c);  -- test SELECT * FROM @tbl; 
Answered on July 17, 2020.
Add Comment

Something like this:

declare @doc xml = '<?xml version="1.0" encoding="utf-8" standalone="yes"?> <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">   <BkToCstmrStmt>     <GrpHdr>       ..........     </GrpHdr>     <Stmt>       <Ntry>         <Amt Ccy="EUR">RequestedAmount1</Amt>         <AddtlNtryInf>RequestedInfo1</AddtlNtryInf>       </Ntry>       <Ntry>         <Amt Ccy="EUR">RequestedAmount2</Amt>         <AddtlNtryInf>RequestedInfo2</AddtlNtryInf>       </Ntry>       <Ntry>         <Amt Ccy="EUR">RequestedAmount3</Amt>         <AddtlNtryInf>RequestedInfo3</AddtlNtryInf>       </Ntry>     </Stmt>   </BkToCstmrStmt>  </Document>';  with xmlnamespaces (DEFAULT 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02')   select s.Stmt.value('(GrpHdr)[1]', 'varchar(200)') GrpHdr,        n.Ntry.value('(Amt)[1]', 'varchar(200)') Amt,        n.Ntry.value('(AddtlNtryInf)[1]', 'varchar(200)') AddtlNtryInf from @doc.nodes('/Document/BkToCstmrStmt') s(Stmt) outer apply s.Stmt.nodes('Stmt/Ntry') n(Ntry) 
Answered on July 17, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.