如何使用动态 t-sql 解析文件夹中的 XML 文件?
How to parse XML file in a folder with dynamic t-sql?
我想将下面的 XML 更改为 SQL 语句以使用路径而不是文件名,目前它正在寻找 'C:\Test\XML\PT38.xml'.
我需要它来解析文件夹中的任何 .XML 文件,而不是查找特定文件。一次只有一个文件,但它们会有不同的名称(数量增加:PT39、PT40 等)。
我尝试为路径添加一个变量,然后更改 BULK 以查找该变量,但它按预期失败了。
我读过一些关于创建临时 table 然后解析日期的内容,但我不确定这是否适合我。
非常感谢您的帮助。
这是我试过的:
DECLARE @xmlFileName varchar(100) = 'C:\Test\XML\'
FROM OPENROWSET(BULK ''' + @xmlFileName + ''', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
这是XML内容:
<?xml version="1.0" encoding="UTF-8" ?>
<MOD1>
<DC BEGIN="1">
<DC4 SEGMENT="1">
<TABNAM>DC4</TABNAM>
<DOCNUM>0000003899888135</DOCNUM>
</DC4>
<ZPR SEGMENT="1">
<AUFNR>000915229446</AUFNR>
<LNO>RM01PL01</LNO>
<CHARG>0006186588</CHARG>
<STR2>211609</STR2>
<QTY>4166.000</QTY>
<PLN_ORDER>6963701111</PLN_ORDER>
</ZPR>
</DC>
</MOD1>
这是SQL table:
CREATE TABLE XMLTESTTABLE
(
PON int,
ASP int,
LTN varchar(11),
GAS int,
QY varchar(15),
LNO varchar(2),
StartTime date,
);
这是声明:
INSERT INTO XMLTESTTABLE(PON, ASP, LTN, GAS, QY, LNO, StartTime)
SELECT ZPRM.value('(AUFNR/text())[1]', 'int')
, ZPRM.value('(CHARG/text())[1]', 'int')
, ZPRM.value('(PLN_ORDER/text())[1]', 'VARCHAR(10)')
, ZPRM.value('(CHARG/text())[1]', 'int')
, ZPRM.value('(QTY/text())[1]', 'DECIMAL(10,0)') AS [qty]
, RIGHT(ZPRM.value('(LNO/text())[1]', 'VARCHAR(10)'), 2) AS [LNO]
, TRY_CAST(STUFF(STUFF(ZPRM.value('(STR2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME)
FROM (SELECT TRY_CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'C:\Test\XML\PT38.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('/MOD1/DC/ZPR') AS MY_XML(ZPRM);
在 SQL Server 2017 及更高版本中更容易实现。它有更好的 API 来处理文件系统。
请尝试以下解决方案。它将在 SQL Server 2012 中运行。
我将 StartTime 列数据类型修改为 TIME(0)
。
您需要修改 @folder 变量值以匹配您环境中的值。
SQL
USE tempdb;
GO
DROP TABLE IF EXISTS dbo.XMLTESTTABLE;
CREATE TABLE dbo.XMLTESTTABLE
(
PON varchar(10),
ASP int,
LTN varchar(11),
GAS int,
QY varchar(15),
LNO varchar(2),
StartTime TIME(0)
);
DECLARE @xml XML
, @sql NVARCHAR(MAX)
, @XMLfileName VARCHAR(256) -- 'e:\Temp\TradeFeed\PT38.xml';
, @folder VARCHAR(256) = 'e:\Temp\TradeFeed';
DECLARE @tbl TABLE (
id INT IDENTITY(1,1) PRIMARY KEY,
[fileName] VARCHAR(512),
depth INT,
isfile BIT
);
INSERT INTO @tbl ([fileName], depth, isfile)
EXEC master.sys.xp_dirtree @folder,1,1;
-- just to see
SELECT * FROM @tbl;
-- filter out not need files
SELECT TOP(1) @XMLfileName = CONCAT(@folder, '\', [fileName])
FROM @tbl
WHERE isfile = 1
AND [fileName] LIKE '%.xml';
SET @sql = N'SELECT @xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(@XMLfileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';
EXEC master.sys.sp_executesql @sql, N'@xmlOut XML OUTPUT', @xmlOut = @xml OUTPUT;
INSERT INTO XMLTESTTABLE(PON, ASP, LTN, GAS, QY, LNO, StartTime)
SELECT @xml.value('(/MOD1/DC/DC4/TABNAM/text())[1]', 'VARCHAR(10)')
, c.value('(CHARG/text())[1]', 'int')
, c.value('(PLN_ORDER/text())[1]', 'VARCHAR(10)')
, c.value('(CHARG/text())[1]', 'int')
, c.value('(QTY/text())[1]', 'DECIMAL(10,0)') AS [qty]
, RIGHT(c.value('(LNO/text())[1]', 'VARCHAR(10)'), 2) AS [LNO]
, TRY_CAST(STUFF(STUFF(c.value('(STR2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME(0))
FROM @xml.nodes('/MOD1/DC/ZPR') AS t(c);
-- test
SELECT * FROM dbo.XMLTESTTABLE;
输出
+-----------+---------+------------+---------+------+-----+-----------+
| PON | ASP | LTN | GAS | QY | LNO | StartTime |
+-----------+---------+------------+---------+------+-----+-----------+
| DC4 | 6186588 | 6963701111 | 6186588 | 4166 | 01 | 21:16:09 |
+-----------+---------+------------+---------+------+-----+-----------+
我想将下面的 XML 更改为 SQL 语句以使用路径而不是文件名,目前它正在寻找 'C:\Test\XML\PT38.xml'.
我需要它来解析文件夹中的任何 .XML 文件,而不是查找特定文件。一次只有一个文件,但它们会有不同的名称(数量增加:PT39、PT40 等)。
我尝试为路径添加一个变量,然后更改 BULK 以查找该变量,但它按预期失败了。
我读过一些关于创建临时 table 然后解析日期的内容,但我不确定这是否适合我。
非常感谢您的帮助。
这是我试过的:
DECLARE @xmlFileName varchar(100) = 'C:\Test\XML\'
FROM OPENROWSET(BULK ''' + @xmlFileName + ''', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
这是XML内容:
<?xml version="1.0" encoding="UTF-8" ?>
<MOD1>
<DC BEGIN="1">
<DC4 SEGMENT="1">
<TABNAM>DC4</TABNAM>
<DOCNUM>0000003899888135</DOCNUM>
</DC4>
<ZPR SEGMENT="1">
<AUFNR>000915229446</AUFNR>
<LNO>RM01PL01</LNO>
<CHARG>0006186588</CHARG>
<STR2>211609</STR2>
<QTY>4166.000</QTY>
<PLN_ORDER>6963701111</PLN_ORDER>
</ZPR>
</DC>
</MOD1>
这是SQL table:
CREATE TABLE XMLTESTTABLE
(
PON int,
ASP int,
LTN varchar(11),
GAS int,
QY varchar(15),
LNO varchar(2),
StartTime date,
);
这是声明:
INSERT INTO XMLTESTTABLE(PON, ASP, LTN, GAS, QY, LNO, StartTime)
SELECT ZPRM.value('(AUFNR/text())[1]', 'int')
, ZPRM.value('(CHARG/text())[1]', 'int')
, ZPRM.value('(PLN_ORDER/text())[1]', 'VARCHAR(10)')
, ZPRM.value('(CHARG/text())[1]', 'int')
, ZPRM.value('(QTY/text())[1]', 'DECIMAL(10,0)') AS [qty]
, RIGHT(ZPRM.value('(LNO/text())[1]', 'VARCHAR(10)'), 2) AS [LNO]
, TRY_CAST(STUFF(STUFF(ZPRM.value('(STR2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME)
FROM (SELECT TRY_CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'C:\Test\XML\PT38.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('/MOD1/DC/ZPR') AS MY_XML(ZPRM);
在 SQL Server 2017 及更高版本中更容易实现。它有更好的 API 来处理文件系统。
请尝试以下解决方案。它将在 SQL Server 2012 中运行。
我将 StartTime 列数据类型修改为 TIME(0)
。
您需要修改 @folder 变量值以匹配您环境中的值。
SQL
USE tempdb;
GO
DROP TABLE IF EXISTS dbo.XMLTESTTABLE;
CREATE TABLE dbo.XMLTESTTABLE
(
PON varchar(10),
ASP int,
LTN varchar(11),
GAS int,
QY varchar(15),
LNO varchar(2),
StartTime TIME(0)
);
DECLARE @xml XML
, @sql NVARCHAR(MAX)
, @XMLfileName VARCHAR(256) -- 'e:\Temp\TradeFeed\PT38.xml';
, @folder VARCHAR(256) = 'e:\Temp\TradeFeed';
DECLARE @tbl TABLE (
id INT IDENTITY(1,1) PRIMARY KEY,
[fileName] VARCHAR(512),
depth INT,
isfile BIT
);
INSERT INTO @tbl ([fileName], depth, isfile)
EXEC master.sys.xp_dirtree @folder,1,1;
-- just to see
SELECT * FROM @tbl;
-- filter out not need files
SELECT TOP(1) @XMLfileName = CONCAT(@folder, '\', [fileName])
FROM @tbl
WHERE isfile = 1
AND [fileName] LIKE '%.xml';
SET @sql = N'SELECT @xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(@XMLfileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';
EXEC master.sys.sp_executesql @sql, N'@xmlOut XML OUTPUT', @xmlOut = @xml OUTPUT;
INSERT INTO XMLTESTTABLE(PON, ASP, LTN, GAS, QY, LNO, StartTime)
SELECT @xml.value('(/MOD1/DC/DC4/TABNAM/text())[1]', 'VARCHAR(10)')
, c.value('(CHARG/text())[1]', 'int')
, c.value('(PLN_ORDER/text())[1]', 'VARCHAR(10)')
, c.value('(CHARG/text())[1]', 'int')
, c.value('(QTY/text())[1]', 'DECIMAL(10,0)') AS [qty]
, RIGHT(c.value('(LNO/text())[1]', 'VARCHAR(10)'), 2) AS [LNO]
, TRY_CAST(STUFF(STUFF(c.value('(STR2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME(0))
FROM @xml.nodes('/MOD1/DC/ZPR') AS t(c);
-- test
SELECT * FROM dbo.XMLTESTTABLE;
输出
+-----------+---------+------------+---------+------+-----+-----------+
| PON | ASP | LTN | GAS | QY | LNO | StartTime |
+-----------+---------+------------+---------+------+-----+-----------+
| DC4 | 6186588 | 6963701111 | 6186588 | 4166 | 01 | 21:16:09 |
+-----------+---------+------------+---------+------+-----+-----------+