在交叉应用上使用嵌套节点查询 XML
Query XML with nested nodes on Cross Apply
给定一个 XML 结构如下:
<ROOT_NODE>
<FOLDER_LIST>
<FOLDER>
<CODE_FOLDER>1</CODE_FOLDER>
<DESCRIPTION>This is a folder</DESCRIPTION>
<DATA_LIST>
<DATA>
<CODE_DATA>100</CODE_DATA>
<OPTIONS>
<OPTION>
<CODE_OPTION>200</CODE_OPTION>
<PRINT_TEXT>This is a test</PRINT_TEXT>
</OPTION>
<OPTION>
<CODE_OPTION>200</CODE_OPTION>
<PRINT_TEXT>This is a test</PRINT_TEXT>
</OPTION>
</OPTIONS>
</DATA>
</DATA_LIST>
</FOLDER>
</FOLDER_LIST>
</ROOT_NODE>
首先,我使用
将第一层 (FOLDER) 的值放入名为 @tmpFolders 的临时 table 中
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(id)
然后我在@tmpFolders 上声明了一个游标
DECLARE cur CURSOR FOR
SELECT CODE_FOLDER, DESCRIPTION FROM @tmpFolders
OPEN cur
FETCH NEXT FROM cur INTO @codeFolder, @description
WHILE (@@FETCH_STATUS = 0)
在游标内,我使用 CROSS APPLY 将第二级 (DATA) 的值插入到另一个名为 @tmpData
的临时 table
INSERT INTO @tmpData(CODE_DATA)
SELECT data.id.value('CODE_DATA[1]','INT'))
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as data(Id)
到目前为止,一切正常。
现在我需要从第三级(OPTION)获取值并将它们插入另一个名为@tmpOptions 的临时 table
我尝试添加另一个 CROSS APPLY 但没有成功
INSERT INTO @tmpOptions(CODE_OPTION, PRINT_TEXT)
SELECT data.id.value('CODE_DATA[1]','INT')),
option.id.value('CODE_OPTION[1]','INT'))
option.id.value('PRINT_TEXT[1]','VARCHAR(50)'))
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as data(Id)
CROSS APPLY data.Id.nodes('OPTIONS/OPTION') as option(Id)
我没有收到任何错误,所以我不确定我做错了什么。
您发布的代码不正确...
I don't get any errors, so I'm not sure what I'm doing wrong.
有很多右括号,缺少逗号,并且您使用的是保留字,应该像 [option]
一样被引用。这一定会引发错误...
像这样尝试
SELECT [data].id.value('CODE_DATA[1]','INT'),
[option].id.value('CODE_OPTION[1]','INT'),
[option].id.value('PRINT_TEXT[1]','VARCHAR(50)')
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as [data](Id)
CROSS APPLY [data].Id.nodes('OPTIONS/OPTION') as [option](Id)
但是...
您的代码是 - 可能! - 如果有多个 <FOLDER>
或多个 <DATA>
,则不会按照您的预期进行操作。在您的 CURSOR
中,您读取了所有元素,而没有对给定父元素进行任何过滤...
无论如何,这不是你应该做的。尽可能避免CURSOR
!
你的最终目标是什么?如果你想在相关的table中传递这个结构。两者的选项代码是否相同 (200
) 是故意的吗?可能是复制粘贴错误...如果所有内部代码都是唯一的,那就很简单了:
SELECT Fld.value(N'(CODE_FOLDER/text())[1]',N'int') AS Folder_Code
,Fld.value(N'(DESCRIPTION/text())[1]',N'nvarchar(max)') AS Folder_Description
,Dt.value(N'(CODE_DATA/text())[1]',N'int') AS Data_Code
,Opt.value(N'(CODE_OPTION/text())[1]',N'int') AS Option_Code
,Opt.value(N'(PRINT_TEXT/text())[1]',N'nvarchar(max)') AS Option_Text
--Generate running IDs, you might add an existing max id if you have to insert into filled tables
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')) AS FolderId
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')
,Dt.value(N'(CODE_DATA/text())[1]',N'int')) AS DataId
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')
,Dt.value(N'(CODE_DATA/text())[1]',N'int')
,Opt.value(N'(CODE_OPTION/text())[1]',N'int')) AS OptionId
FROM @xml.nodes(N'/ROOT_NODE/FOLDER_LIST/FOLDER') AS A(Fld)
OUTER APPLY Fld.nodes(N'DATA_LIST/DATA') AS B(Dt)
OUTER APPLY Dt.nodes(N'OPTIONS/OPTION') AS C(Opt);
如果内码不唯一,可以这样:
WITH Folders AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS FolderId
,Fld.value(N'(CODE_FOLDER/text())[1]',N'int') AS Folder_Code
,Fld.value(N'(DESCRIPTION/text())[1]',N'nvarchar(max)') AS Folder_Description
,Fld.query(N'DATA_LIST/DATA') AS Node_data
FROM @xml.nodes(N'/ROOT_NODE/FOLDER_LIST/FOLDER') AS A(Fld)
)
,FoldersWithDatas AS
(
SELECT Folders.FolderId
,Folders.Folder_Code
,Folders.Folder_Description
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS DataId
,Dt.value(N'(CODE_DATA/text())[1]',N'int') AS Data_Code
,Dt.query(N'OPTIONS/OPTION') AS Node_data
FROM Folders
OUTER APPLY Folders.Node_data.nodes(N'DATA') AS A(Dt)
)
SELECT FoldersWithDatas.FolderId
,FoldersWithDatas.Folder_Code
,FoldersWithDatas.Folder_Description
,FoldersWithDatas.DataId
,FoldersWithDatas.Data_Code
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS OptionId
,Dt.value(N'(CODE_OPTION/text())[1]',N'int') AS Option_Code
FROM FoldersWithDatas
OUTER APPLY FoldersWithDatas.Node_data.nodes(N'OPTION') AS A(Dt);
这适用于任意数量的文件夹、嵌套数据和嵌套选项...
将其写入临时 table 并使用 SELECT DISTINCT
将每组数据连同适当的外键插入其 table.
给定一个 XML 结构如下:
<ROOT_NODE>
<FOLDER_LIST>
<FOLDER>
<CODE_FOLDER>1</CODE_FOLDER>
<DESCRIPTION>This is a folder</DESCRIPTION>
<DATA_LIST>
<DATA>
<CODE_DATA>100</CODE_DATA>
<OPTIONS>
<OPTION>
<CODE_OPTION>200</CODE_OPTION>
<PRINT_TEXT>This is a test</PRINT_TEXT>
</OPTION>
<OPTION>
<CODE_OPTION>200</CODE_OPTION>
<PRINT_TEXT>This is a test</PRINT_TEXT>
</OPTION>
</OPTIONS>
</DATA>
</DATA_LIST>
</FOLDER>
</FOLDER_LIST>
</ROOT_NODE>
首先,我使用
将第一层 (FOLDER) 的值放入名为 @tmpFolders 的临时 table 中FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(id)
然后我在@tmpFolders 上声明了一个游标
DECLARE cur CURSOR FOR
SELECT CODE_FOLDER, DESCRIPTION FROM @tmpFolders
OPEN cur
FETCH NEXT FROM cur INTO @codeFolder, @description
WHILE (@@FETCH_STATUS = 0)
在游标内,我使用 CROSS APPLY 将第二级 (DATA) 的值插入到另一个名为 @tmpData
的临时 tableINSERT INTO @tmpData(CODE_DATA)
SELECT data.id.value('CODE_DATA[1]','INT'))
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as data(Id)
到目前为止,一切正常。 现在我需要从第三级(OPTION)获取值并将它们插入另一个名为@tmpOptions 的临时 table 我尝试添加另一个 CROSS APPLY 但没有成功
INSERT INTO @tmpOptions(CODE_OPTION, PRINT_TEXT)
SELECT data.id.value('CODE_DATA[1]','INT')),
option.id.value('CODE_OPTION[1]','INT'))
option.id.value('PRINT_TEXT[1]','VARCHAR(50)'))
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as data(Id)
CROSS APPLY data.Id.nodes('OPTIONS/OPTION') as option(Id)
我没有收到任何错误,所以我不确定我做错了什么。
您发布的代码不正确...
I don't get any errors, so I'm not sure what I'm doing wrong.
有很多右括号,缺少逗号,并且您使用的是保留字,应该像 [option]
一样被引用。这一定会引发错误...
像这样尝试
SELECT [data].id.value('CODE_DATA[1]','INT'),
[option].id.value('CODE_OPTION[1]','INT'),
[option].id.value('PRINT_TEXT[1]','VARCHAR(50)')
FROM @xml.nodes('ROOT_NODE/FOLDER_LIST/FOLDER') as folder(Id)
CROSS APPLY folder.Id.nodes('DATA_LIST/DATA') as [data](Id)
CROSS APPLY [data].Id.nodes('OPTIONS/OPTION') as [option](Id)
但是...
您的代码是 - 可能! - 如果有多个 <FOLDER>
或多个 <DATA>
,则不会按照您的预期进行操作。在您的 CURSOR
中,您读取了所有元素,而没有对给定父元素进行任何过滤...
无论如何,这不是你应该做的。尽可能避免CURSOR
!
你的最终目标是什么?如果你想在相关的table中传递这个结构。两者的选项代码是否相同 (200
) 是故意的吗?可能是复制粘贴错误...如果所有内部代码都是唯一的,那就很简单了:
SELECT Fld.value(N'(CODE_FOLDER/text())[1]',N'int') AS Folder_Code
,Fld.value(N'(DESCRIPTION/text())[1]',N'nvarchar(max)') AS Folder_Description
,Dt.value(N'(CODE_DATA/text())[1]',N'int') AS Data_Code
,Opt.value(N'(CODE_OPTION/text())[1]',N'int') AS Option_Code
,Opt.value(N'(PRINT_TEXT/text())[1]',N'nvarchar(max)') AS Option_Text
--Generate running IDs, you might add an existing max id if you have to insert into filled tables
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')) AS FolderId
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')
,Dt.value(N'(CODE_DATA/text())[1]',N'int')) AS DataId
,DENSE_RANK() OVER(ORDER BY Fld.value(N'(CODE_FOLDER/text())[1]',N'int')
,Dt.value(N'(CODE_DATA/text())[1]',N'int')
,Opt.value(N'(CODE_OPTION/text())[1]',N'int')) AS OptionId
FROM @xml.nodes(N'/ROOT_NODE/FOLDER_LIST/FOLDER') AS A(Fld)
OUTER APPLY Fld.nodes(N'DATA_LIST/DATA') AS B(Dt)
OUTER APPLY Dt.nodes(N'OPTIONS/OPTION') AS C(Opt);
如果内码不唯一,可以这样:
WITH Folders AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS FolderId
,Fld.value(N'(CODE_FOLDER/text())[1]',N'int') AS Folder_Code
,Fld.value(N'(DESCRIPTION/text())[1]',N'nvarchar(max)') AS Folder_Description
,Fld.query(N'DATA_LIST/DATA') AS Node_data
FROM @xml.nodes(N'/ROOT_NODE/FOLDER_LIST/FOLDER') AS A(Fld)
)
,FoldersWithDatas AS
(
SELECT Folders.FolderId
,Folders.Folder_Code
,Folders.Folder_Description
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS DataId
,Dt.value(N'(CODE_DATA/text())[1]',N'int') AS Data_Code
,Dt.query(N'OPTIONS/OPTION') AS Node_data
FROM Folders
OUTER APPLY Folders.Node_data.nodes(N'DATA') AS A(Dt)
)
SELECT FoldersWithDatas.FolderId
,FoldersWithDatas.Folder_Code
,FoldersWithDatas.Folder_Description
,FoldersWithDatas.DataId
,FoldersWithDatas.Data_Code
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS OptionId
,Dt.value(N'(CODE_OPTION/text())[1]',N'int') AS Option_Code
FROM FoldersWithDatas
OUTER APPLY FoldersWithDatas.Node_data.nodes(N'OPTION') AS A(Dt);
这适用于任意数量的文件夹、嵌套数据和嵌套选项...
将其写入临时 table 并使用 SELECT DISTINCT
将每组数据连同适当的外键插入其 table.