解析 SQL 中 XML 的选定值

Parse selected values from XML in SQL

我有以下 XML 文件(我在这里只放了一个简短的方案),它是一份 SEPA XML 银行对账单。我不熟悉解析 XML 文件,我的下一步是插入和比较存储在 SQL 数据库中的数据以进行错误检查。可悲的是,我知道下一步该怎么做,不知道如何迈出第一步。我只需要从存储在特定位置的文件中创建 table 到 select 的 2 个属性值

<?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>

如果XML结构更简单,例如像这样...

<?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>

...然后我将使用此查询 select 请求的属性 Amt 和 AddtlNtryInf,它完美地工作

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);

但不知道如何处理那个更复杂的问题。我试过类似的东西和几次类似的尝试但我失败了,因为它没有 select 任何东西,结果什么都没有

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);

不知道如何处理该 CROSS APPLY。非常感谢您的任何建议或改进,您做得很好

像这样:

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)

你快到了。

(1) XML 文件有默认命名空间,需要通过 XMLNAMESPACES 子句进行特殊处理。

(2) Amt 元素可能有一个数值,因此您可以使用 DECIMAL(x,y) 数据类型。但我保留了 NVARCHAR(255) 以匹配混淆的 XML 文件示例。

(3) 下面的 SQL 是使用 .value() 方法,没有不必要的 .query() 方法。

(4) 出于性能原因,使用 elementName/text() 技术是一种很好的做法。这是 MS SQL 服务器特定的特性。

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;