【问题标题】:CTE and FOR XML to generate nested XMLCTE 和 FOR XML 生成嵌套 XML
【发布时间】:2013-01-23 19:51:27
【问题描述】:

我在数据库中有一个邻接列表,并希望通过 SQL SP 将 XML 格式的数据传递给客户端。我正在尝试使用 CTE 和 FOR XML,但我没有让 XML 节点嵌套。

仅供参考,这将代表一个站点地图。

表结构:

CREATE TABLE [dbo].[PageHierarchy](
    [ModuleId] [int] NOT NULL,
    [PageId] [int] IDENTITY(1,1) NOT NULL,
    [ParentPageId] [int] NULL,
    [PageUrl] [nvarchar](100) NULL,
    [PageTitle] [nvarchar](50) NOT NULL,
    [PageOrder] [int] NULL)

以及 CTE 的开始:

;WITH cte AS
(
    select * from PageHierarchy where ParentPageId is null
    union all
    select child.* from PageHierarchy child inner join cte parent on parent.PageId = child.ParentPageId
)
SELECT ModuleId, PageId, ParentPageId, PageUrl, PageTitle, PageOrder FROM cte
group by ModuleId, PageId, ParentPageId, PageUrl, PageTitle, PageOrder
order by PageOrder
for xml auto, root ('bob')

生成如下所示的 XML:

<bob>
  <cte ModuleId="1" PageId="1" PageUrl="~/Admin/" PageTitle="Administration" PageOrder="1000" />
  <cte ModuleId="1" PageId="4" ParentPageId="1" PageTitle="Manage Users" PageOrder="1030" />
  <cte ModuleId="1" PageId="5" ParentPageId="4" PageUrl="~/Admin/AddUser" PageTitle="Add Users" PageOrder="1040" />
  <cte ModuleId="1" PageId="8" ParentPageId="4" PageUrl="~/Admin/EditUser" PageTitle="Edit/Search User" PageOrder="1070" />
</bob>

当我想要的是看起来像这样的 XML 时:

<bob>
  <cte ModuleId="1" PageId="1" PageUrl="~/Admin/" PageTitle="Administration" PageOrder="1000" />
  <cte ModuleId="1" PageId="4" ParentPageId="1" PageTitle="Manage Users" PageOrder="1030" >
    <cte ModuleId="1" PageId="5" ParentPageId="4" PageUrl="~/Admin/AddUser" PageTitle="Add Users" PageOrder="1040" />
    <cte ModuleId="1" PageId="8" ParentPageId="4" PageUrl="~/Admin/EditUser" PageTitle="Edit/Search User" PageOrder="1070" />
  </cte>
</bob>

我猜问题不在于 CTE,而在于 select,但我不知道从哪里开始修复它。另外,我不知道嵌套有多深,所以我假设我需要它来支持至少 10 层深度。

编辑 1:
我想我越来越近了......在查看this 页面时,我创建了一个UDF,但仍然存在一些问题:

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

以及调用 UDF 的 SQL

SELECT ModuleId AS "@ModuleId", PageId AS "@PageId",
    ParentPageId AS "@ParentPageId", PageUrl AS "@PageUrl",
    PageTitle AS "@PageTitle", PageOrder AS "@PageOrder", 
    dbo.PageHierarchyNode(PageId)
FROM PageHierarchy
FOR XML PATH('Page'), ROOT('SiteMap'), TYPE

这将为我嵌套 XML,但它正在复制节点,这不是我想要的..

编辑 2:

我只需要在调用 UDF 的 SELECT 中添加一个 WHERE 子句:

...
WHERE ParentPageId IS NULL

【问题讨论】:

    标签: sql-server xml tsql common-table-expression for-xml


    【解决方案1】:

    递归 CTE 不像“嵌套”中那样递归,它们的工作方式不同,您尝试做的事情不适用于 CTE。 (把它们想象成总是tail-recursive。)

    我发现在 SQL Server 中构建递归 XML 的唯一方法是创建一个以递归方式呈现节点的标量函数;函数可以进行递归调用,以便按预期工作。

    【讨论】:

      【解决方案2】:

      原来我根本不想要 CTE,只是我递归调用的 UDF

      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
      

      使用调用 UDF 的 SQL 为

      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
      

      【讨论】:

        【解决方案3】:

        这个问题以及 OP 的回答对我帮助很大。由于我缺少一些上下文,我花了一点时间来掌握答案。所以这里有一个单独的答案和更通用的解释(我试图删除与在 XML 输出中获取分层数据没有直接相关的所有代码)。


        假设分层数据有以下典型表:

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

        假设它有以下数据:

        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');
        

        要获取分层 XML 数据,我们可以使用以下函数:

        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;
        

        可以这样调用:

        SELECT dbo.fn_EmployeeHierarchyNode(1)
        

        或者,如果你也想要根节点,像这样:

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

        这会产生:

        <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>
        

        【讨论】:

          猜你喜欢
          • 2019-01-02
          • 2013-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-10
          • 2017-08-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多