解析 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;
我有以下 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;