【问题标题】:Parse selected values from XML in SQL在 SQL 中解析 XML 中的选定值
【发布时间】:2020-07-04 20:28:01
【问题描述】:

我有以下 XML 文件(我在这里只放了一个简短的方案),它是 SEPA XML 银行对帐单。我不熟悉解析 XML 文件,我下一步将插入和比较存储在 SQL 数据库中的数据以进行错误检查。可悲的是,我知道下一步该做什么,不知道如何迈出第一步。我只需要创建一个表来从存储在特定位置的文件中选择 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>

...然后我将使用此查询来选择请求的属性 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
   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。非常感谢您的任何建议或改进,您做得很好

【问题讨论】:

    标签: sql sql-server xml xml-parsing


    【解决方案1】:

    你快到了。

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

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

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

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

    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;
    

    【讨论】:

    • 你说'你快到了'吗? :D 一点也不,但多亏了你,我是。非常感谢您的建议,这绝对是绝妙的,我非常感谢。是的,此时数据类型对我来说并不重要,我正在为整个选择而苦苦挣扎。再次感谢您。
    【解决方案2】:

    类似这样的:

    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)
    

    【讨论】:

    • 非常感谢大卫的这个查​​询,你教会了我一种替代方法,它对我帮助很大。它完美地工作。你做得很好
    猜你喜欢
    • 2021-08-05
    • 2018-09-11
    • 2013-03-18
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    相关资源
    最近更新 更多