XML 至 SQL Table 查询
XML to SQL Table Query
这是我的XML存储的一行。如何使用 T-SQL 查询将其转换为插入 table,格式如下 table?
<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>
这是要求的输出格式。
问题是我在原始 xml 中没有记录分隔符。每条新记录都以 <DSPVCHDATE>
开头
您可以使用 outer apply
导航 xml 内容的嵌套元素。
考虑到这个 XML 的不方便结构,可以通过添加一个名为 <ThisNode>
.
的包含节点,将其更改为如下可用的东西
DECLARE @XML XML = '
<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>'
这可以转换为可用的 XML,如下所示:
WITH
cte AS (Select REPLACE(REPLACE(CONVERT(NVARCHAR(MAX), @XML, 1), N'<DSPVCHDATE>', '
</ThisNode>
<ThisNode>
<DSPVCHDATE>'), N'</ENVELOPE>', N'
</ThisNode>
</ENVELOPE>') AS str)
SELECT @XML = CAST(STUFF(str, CHARINDEX(N'</ThisNode>', str), LEN(N'</ThisNode>'), N'') AS XML)
FROM cte
;
查询
SELECT
A.evnt.value('(DSPVCHDATE/text())[1]','nvarchar(100)') as DSPVCHDATE
,A.evnt.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,A.evnt.value('(DSPVCHTYPE/text())[1]','nvarchar(100)') as DSPVCHTYPE
,A.evnt.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,A.evnt.value('(DSPEXPLVCHNUMBER/text())[1]','nvarchar(100)') as DSPEXPLVCHNUMBER
,B.rec.value('(DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
,B.rec.value('(DSPVCHINAMT/text())[1]','nvarchar(100)') AS DSPVCHINAMT
,C.rec.value('(DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
,C.rec.value('(DSPVCHNETTOUTAMT/text())[1]','float') AS DSPVCHNETTOUTAMT
,D.rec.value('(DSPVCHCLQTY/text())[1]','nvarchar(100)') AS DSPVCHCLQTY
,D.rec.value('(DSPVCHCLAMT/text())[1]','int') AS DSPVCHCLAMT
FROM @XML.nodes('/ENVELOPE/ThisNode') A(evnt)
OUTER APPLY A.evnt.nodes('DSPINBLOCK') B(rec)
OUTER APPLY A.evnt.nodes('DSPOUTBLOCK') C(rec)
OUTER APPLY A.evnt.nodes('DSPCLBLOCK') D(rec)
演示在 db<>fiddle
这是使用纯 XQuery 的另一种方法。无需进行任何字符串操作、CASTing 等
根元素<ENVELOPE>
内的所有元素构成等差数列。按位置分组的元素:1 - 7、8 - 14 等应放置在包围 <row>
元素内。
它会即时创建以下 XML:
<ENVELOPE>
<row>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
...
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
</row>
<row>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
...
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</row>
</ENVELOPE>
SQL
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO @tbl (xmldata) VALUES
(N'<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>');
SELECT ID --, x
, c.value('(DSPVCHDATE/text())[1]','nvarchar(100)') as DSPVCHDATE
,c.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,c.value('(DSPVCHTYPE/text())[1]','nvarchar(100)') as DSPVCHTYPE
,c.value('(DSPINBLOCK/DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
,c.value('(DSPINBLOCK/DSPVCHINAMT/text())[1]','decimal(12,2)') AS DSPVCHINAMT
,c.value('(DSPOUTBLOCK/DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
,c.value('(DSPOUTBLOCK/DSPVCHNETTOUTAMT/text())[1]','decimal(12,2)') AS DSPVCHNETTOUTAMT
,c.value('(DSPEXPLVCHNUMBER/text())[1]','nvarchar(100)') as DSPEXPLVCHNUMBER
--,c.value('(DSPCLBLOCK/DSPVCHCLQTY/text())[1]','nvarchar(100)') AS DSPVCHCLQTY
--,c.value('(DSPCLBLOCK/DSPVCHCLAMT/text())[1]','int') AS DSPVCHCLAMT
FROM @tbl
CROSS APPLY (SELECT xmldata.query('<ENVELOPE>
{
for $x in /ENVELOPE/DSPVCHDATE
let $pos := count(ENVELOPE/DSPVCHDATE[. << $x]) + 1
let $start := 1 + 7 * ($pos -1)
let $end := 7 * $pos
return <row>{/ENVELOPE/*[position() ge $start and position() le $end]}</row>
}
</ENVELOPE>')) AS t1(x)
CROSS APPLY t1.x.nodes('/ENVELOPE/row') AS t2(c);
输出
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
| ID | DSPVCHDATE | DSPVCHITEMACCOUNT | DSPVCHTYPE | DSPVCHINQTY | DSPVCHINAMT | DSPVCHOUTQTY | DSPVCHNETTOUTAMT | DSPEXPLVCHNUMBER |
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
| 1 | 16-4-2021 | PRASHANT MEHTA 359244 | Sale | NULL | NULL | 1 Pcs | 23046.88 | (No. :IV2612) |
| 1 | 19-4-2021 | XYZ Company | Purchase | 1 Pcs | 23437.50 | NULL | NULL | (No. :IV2613) |
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
SQL#2
基于@Charlieface 的想法。
WITH rs AS
(
SELECT ID, xmldata
, c.value('for $i in . return count(../*[. << $i]) + 1', 'INT') AS pos
FROM @tbl
CROSS APPLY xmldata.nodes('/ENVELOPE/DSPVCHDATE') AS t(c)
)
SELECT ID
, c.value('(/ENVELOPE/*[sql:column("pos")]/text())[1]','nvarchar(100)') AS DSPVCHDATE
, c.value('(/ENVELOPE/*[sql:column("pos") + 1]/text())[1]','nvarchar(100)') AS DSPVCHITEMACCOUNT
, c.value('(/ENVELOPE/*[sql:column("pos") + 2]/text())[1]','nvarchar(100)') AS DSPVCHTYPE
, c.value('(/ENVELOPE/*[sql:column("pos") + 3]/DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
, c.value('(/ENVELOPE/*[sql:column("pos") + 3]/DSPVCHINAMT/text())[1]','decimal(12,2)') AS DSPVCHINAMT
, c.value('(/ENVELOPE/*[sql:column("pos") + 4]/DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
, c.value('(/ENVELOPE/*[sql:column("pos") + 4]/DSPVCHNETTOUTAMT/text())[1]','nvarchar(100)') AS DSPVCHNETTOUTAMT
, c.value('(/ENVELOPE/*[sql:column("pos") + 6]/text())[1]','nvarchar(100)') AS DSPEXPLVCHNUMBER
FROM rs
CROSS APPLY xmldata.nodes('/ENVELOPE') AS t(c);
这是我的XML存储的一行。如何使用 T-SQL 查询将其转换为插入 table,格式如下 table?
<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>
这是要求的输出格式。
问题是我在原始 xml 中没有记录分隔符。每条新记录都以 <DSPVCHDATE>
您可以使用 outer apply
导航 xml 内容的嵌套元素。
考虑到这个 XML 的不方便结构,可以通过添加一个名为 <ThisNode>
.
DECLARE @XML XML = '
<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>'
这可以转换为可用的 XML,如下所示:
WITH
cte AS (Select REPLACE(REPLACE(CONVERT(NVARCHAR(MAX), @XML, 1), N'<DSPVCHDATE>', '
</ThisNode>
<ThisNode>
<DSPVCHDATE>'), N'</ENVELOPE>', N'
</ThisNode>
</ENVELOPE>') AS str)
SELECT @XML = CAST(STUFF(str, CHARINDEX(N'</ThisNode>', str), LEN(N'</ThisNode>'), N'') AS XML)
FROM cte
;
查询
SELECT
A.evnt.value('(DSPVCHDATE/text())[1]','nvarchar(100)') as DSPVCHDATE
,A.evnt.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,A.evnt.value('(DSPVCHTYPE/text())[1]','nvarchar(100)') as DSPVCHTYPE
,A.evnt.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,A.evnt.value('(DSPEXPLVCHNUMBER/text())[1]','nvarchar(100)') as DSPEXPLVCHNUMBER
,B.rec.value('(DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
,B.rec.value('(DSPVCHINAMT/text())[1]','nvarchar(100)') AS DSPVCHINAMT
,C.rec.value('(DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
,C.rec.value('(DSPVCHNETTOUTAMT/text())[1]','float') AS DSPVCHNETTOUTAMT
,D.rec.value('(DSPVCHCLQTY/text())[1]','nvarchar(100)') AS DSPVCHCLQTY
,D.rec.value('(DSPVCHCLAMT/text())[1]','int') AS DSPVCHCLAMT
FROM @XML.nodes('/ENVELOPE/ThisNode') A(evnt)
OUTER APPLY A.evnt.nodes('DSPINBLOCK') B(rec)
OUTER APPLY A.evnt.nodes('DSPOUTBLOCK') C(rec)
OUTER APPLY A.evnt.nodes('DSPCLBLOCK') D(rec)
演示在 db<>fiddle
这是使用纯 XQuery 的另一种方法。无需进行任何字符串操作、CASTing 等
根元素<ENVELOPE>
内的所有元素构成等差数列。按位置分组的元素:1 - 7、8 - 14 等应放置在包围 <row>
元素内。
它会即时创建以下 XML:
<ENVELOPE>
<row>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
...
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
</row>
<row>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
...
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</row>
</ENVELOPE>
SQL
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO @tbl (xmldata) VALUES
(N'<ENVELOPE>
<DSPVCHDATE>16-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>PRASHANT MEHTA 359244</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Sale</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY></DSPVCHINQTY>
<DSPVCHINAMT></DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY>1 Pcs</DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT>23046.88</DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY></DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2612)</DSPEXPLVCHNUMBER>
<DSPVCHDATE>19-4-2021</DSPVCHDATE>
<DSPVCHITEMACCOUNT>XYZ Company</DSPVCHITEMACCOUNT>
<DSPVCHTYPE>Purchase</DSPVCHTYPE>
<DSPINBLOCK>
<DSPVCHINQTY>1 Pcs</DSPVCHINQTY>
<DSPVCHINAMT>23437.50</DSPVCHINAMT>
</DSPINBLOCK>
<DSPOUTBLOCK>
<DSPVCHOUTQTY></DSPVCHOUTQTY>
<DSPVCHNETTOUTAMT></DSPVCHNETTOUTAMT>
</DSPOUTBLOCK>
<DSPCLBLOCK>
<DSPVCHCLQTY>0 Pcs</DSPVCHCLQTY>
<DSPVCHCLAMT></DSPVCHCLAMT>
</DSPCLBLOCK>
<DSPEXPLVCHNUMBER>(No. :IV2613)</DSPEXPLVCHNUMBER>
</ENVELOPE>');
SELECT ID --, x
, c.value('(DSPVCHDATE/text())[1]','nvarchar(100)') as DSPVCHDATE
,c.value('(DSPVCHITEMACCOUNT/text())[1]','nvarchar(100)') as DSPVCHITEMACCOUNT
,c.value('(DSPVCHTYPE/text())[1]','nvarchar(100)') as DSPVCHTYPE
,c.value('(DSPINBLOCK/DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
,c.value('(DSPINBLOCK/DSPVCHINAMT/text())[1]','decimal(12,2)') AS DSPVCHINAMT
,c.value('(DSPOUTBLOCK/DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
,c.value('(DSPOUTBLOCK/DSPVCHNETTOUTAMT/text())[1]','decimal(12,2)') AS DSPVCHNETTOUTAMT
,c.value('(DSPEXPLVCHNUMBER/text())[1]','nvarchar(100)') as DSPEXPLVCHNUMBER
--,c.value('(DSPCLBLOCK/DSPVCHCLQTY/text())[1]','nvarchar(100)') AS DSPVCHCLQTY
--,c.value('(DSPCLBLOCK/DSPVCHCLAMT/text())[1]','int') AS DSPVCHCLAMT
FROM @tbl
CROSS APPLY (SELECT xmldata.query('<ENVELOPE>
{
for $x in /ENVELOPE/DSPVCHDATE
let $pos := count(ENVELOPE/DSPVCHDATE[. << $x]) + 1
let $start := 1 + 7 * ($pos -1)
let $end := 7 * $pos
return <row>{/ENVELOPE/*[position() ge $start and position() le $end]}</row>
}
</ENVELOPE>')) AS t1(x)
CROSS APPLY t1.x.nodes('/ENVELOPE/row') AS t2(c);
输出
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
| ID | DSPVCHDATE | DSPVCHITEMACCOUNT | DSPVCHTYPE | DSPVCHINQTY | DSPVCHINAMT | DSPVCHOUTQTY | DSPVCHNETTOUTAMT | DSPEXPLVCHNUMBER |
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
| 1 | 16-4-2021 | PRASHANT MEHTA 359244 | Sale | NULL | NULL | 1 Pcs | 23046.88 | (No. :IV2612) |
| 1 | 19-4-2021 | XYZ Company | Purchase | 1 Pcs | 23437.50 | NULL | NULL | (No. :IV2613) |
+----+------------+-----------------------+------------+-------------+-------------+--------------+------------------+------------------+
SQL#2
基于@Charlieface 的想法。
WITH rs AS
(
SELECT ID, xmldata
, c.value('for $i in . return count(../*[. << $i]) + 1', 'INT') AS pos
FROM @tbl
CROSS APPLY xmldata.nodes('/ENVELOPE/DSPVCHDATE') AS t(c)
)
SELECT ID
, c.value('(/ENVELOPE/*[sql:column("pos")]/text())[1]','nvarchar(100)') AS DSPVCHDATE
, c.value('(/ENVELOPE/*[sql:column("pos") + 1]/text())[1]','nvarchar(100)') AS DSPVCHITEMACCOUNT
, c.value('(/ENVELOPE/*[sql:column("pos") + 2]/text())[1]','nvarchar(100)') AS DSPVCHTYPE
, c.value('(/ENVELOPE/*[sql:column("pos") + 3]/DSPVCHINQTY/text())[1]','nvarchar(100)') AS DSPVCHINQTY
, c.value('(/ENVELOPE/*[sql:column("pos") + 3]/DSPVCHINAMT/text())[1]','decimal(12,2)') AS DSPVCHINAMT
, c.value('(/ENVELOPE/*[sql:column("pos") + 4]/DSPVCHOUTQTY/text())[1]','nvarchar(100)') AS DSPVCHOUTQTY
, c.value('(/ENVELOPE/*[sql:column("pos") + 4]/DSPVCHNETTOUTAMT/text())[1]','nvarchar(100)') AS DSPVCHNETTOUTAMT
, c.value('(/ENVELOPE/*[sql:column("pos") + 6]/text())[1]','nvarchar(100)') AS DSPEXPLVCHNUMBER
FROM rs
CROSS APPLY xmldata.nodes('/ENVELOPE') AS t(c);