【问题标题】:Query XML with nested nodes on Cross Apply在 Cross Apply 上使用嵌套节点查询 XML
【发布时间】:2018-03-25 18:06:03
【问题描述】:

给定一个结构如下的 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 的临时表中
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 的临时表中

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 的临时表中 我尝试添加另一个 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)

我没有收到任何错误,所以我不确定自己做错了什么。

【问题讨论】:

    标签: xml sql-server-2008 cursor cross-apply


    【解决方案1】:

    您发布的代码不正确...

    我没有收到任何错误,所以我不确定自己做错了什么。

    有很多右括号,缺少逗号,并且您使用的是保留字,应该像[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)
    

    但是...

    您的代码是 - 可能! - 如果有多个&lt;FOLDER&gt; 或多个&lt;DATA&gt;,则不会按照您的预期进行。在您的CURSOR 中,您可以读取所有元素,而无需对给定父级进行任何过滤...

    无论如何,这不是你应该这样做的方式。尽可能避免CURSOR

    你的最终目标是什么?如果要在相关表中传输此结构。是否故意使两者的选项代码相同(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);
    

    这适用于任意数量的文件夹、嵌套数据和嵌套选项...

    将其写入临时表并使用SELECT DISTINCT 将每组数据连同适当的外键一起插入其表中。

    【讨论】:

    • 我原来的xml文件中没有语法错误(我发的只是我当场写的简化版)。后来我发现我写的东西确实有效,我在 where 子句中犯了一个复制/粘贴错误。无论如何,我对你的建议很感兴趣。我一试就会告诉你。
    • 我刚刚测试过,它就像一个魅力。比我使用的方法更容易和更快。谢谢!
    猜你喜欢
    • 2022-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-03
    • 2019-06-09
    相关资源
    最近更新 更多