CTE and FOR XML to generate nested XML

前端 未结 3 1256
你的背包
你的背包 2020-12-16 01:22

I have an adjacency list in the DB and want to deliver the data in XML format to the client through a SQL SP. I\'m trying to use CTE and FOR XML but I am not getting the XM

相关标签:
3条回答
  • 2020-12-16 01:49

    Recursive CTEs are not recursive as in "nested", they work differently and what you're trying to do doesn't work with CTEs. (Think of them as being always tail-recursive.)

    The only way I have found to build recursive XML in SQL Server is by creating a scalar function which renders the nodes recursively; functions can make recursive calls so that this works as expected.

    0 讨论(0)
  • 2020-12-16 01:52

    The question as well as the OP's answer helped me a lot. It took me a bit to grasp the answer as I was missing some context. So here's a seperate answer with a more generic explanation (I've tried to remove every bit of code that didn't relate directly to getting hierarchical data in XML output).


    Suppose the following typical table for hierarchical data:

    CREATE TABLE Employee (Id INT, BossId INT, Name NVARCHAR(50));
    

    Suppose it has the following data:

    INSERT INTO Employee (Id, BossId, Name) VALUES
    (1, NULL, 'Boss Pancone'),
    (2, 1, 'Capioregime Luciano'),
    (3, 1, 'Capioregime Bruno'),
    (4, 2, 'Johnny'),
    (5, 2, 'Luca'),
    (6, 2, 'Luciano jr.'),
    (7, 3, 'Marco'),
    (8, 3, 'Mario'),
    (9, 3, 'Giacomo');
    

    To get hierarchical XML data we could use the following function:

    ALTER FUNCTION dbo.fn_EmployeeHierarchyNode (@BossId INT) RETURNS XML
    BEGIN RETURN
        (SELECT Id, 
                BossId, 
                Name,
                dbo.fn_EmployeeHierarchyNode(Id)
            FROM Employee
            WHERE BossId = @BossId
            FOR XML AUTO)
    END;
    

    Which can be called like this:

    SELECT dbo.fn_EmployeeHierarchyNode(1)
    

    Or, if you want the root node as well, like this:

    SELECT  Id,
            BossId,
            Name,
            dbo.fn_EmployeeHierarchyNode(Id)
    FROM    Employee
    WHERE   BossId IS NULL
    FOR     XML AUTO
    

    Which would produce:

    <Employee Id="1" Name="Boss Pancone">
      <Employee Id="2" BossId="1" Name="Capioregime Luciano">
        <Employee Id="4" BossId="2" Name="Johnny" />
        <Employee Id="5" BossId="2" Name="Luca" />
        <Employee Id="6" BossId="2" Name="Luciano jr." />
      </Employee>
      <Employee Id="3" BossId="1" Name="Capioregime Bruno">
        <Employee Id="7" BossId="3" Name="Marco" />
        <Employee Id="8" BossId="3" Name="Mario" />
        <Employee Id="9" BossId="3" Name="Giacomo" />
      </Employee>
    </Employee>
    
    0 讨论(0)
  • 2020-12-16 01:56

    Turns out I didn't want the CTE at all, just a UDF that I call recursively

    CREATE FUNCTION PageHierarchyNode(@PageId int)
    RETURNS XML
    WITH RETURNS NULL ON NULL INPUT 
    BEGIN RETURN 
      (SELECT ModuleId AS "@ModuleId", PageId AS "@PageId",
        ParentPageId AS "@ParentPageId", PageUrl AS "@PageUrl",
        PageTitle AS "@PageTitle", PageOrder AS "@PageOrder", 
          CASE WHEN ParentPageId=@PageId
          THEN dbo.PageHierarchyNode(PageId)
          END
       FROM dbo.PageHierarchy WHERE ParentPageId=@PageId
       FOR XML PATH('Page'), TYPE)
    END
    

    with the SQL that calls the UDF as

    SELECT ModuleId AS "@ModuleId", PageId AS "@PageId",
        ParentPageId AS "@ParentPageId", PageUrl AS "@PageUrl",
        PageTitle AS "@PageTitle", PageOrder AS "@PageOrder", 
        dbo.PageHierarchyNode(PageId)
    FROM PageHierarchy
    WHERE ParentPageId IS NULL
    FOR XML PATH('Page'), ROOT('SiteMap'), TYPE
    
    0 讨论(0)
提交回复
热议问题