Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 将平面表转换为编码父子关系的XML,但不带继承人标记_Sql_Sql Server_Xml_Tsql - Fatal编程技术网

Sql 将平面表转换为编码父子关系的XML,但不带继承人标记

Sql 将平面表转换为编码父子关系的XML,但不带继承人标记,sql,sql-server,xml,tsql,Sql,Sql Server,Xml,Tsql,我要求将一个平面表转换为一个特殊格式的XML,该XML显示了父子关系,但具有一个平面标记结构 我生成了这个SQL,它将结果磨碎,我添加了一些虚拟数据生成,所以它将独立运行 DECLARE @root varchar(250); SET @root = 'AB&C'; DECLARE @index_base bigint; SET @index_base = 9999; DECLARE @tbl TABLE ( [FUNCTION] VARCHAR(20) ,[SUBF

我要求将一个平面表转换为一个特殊格式的XML,该XML显示了父子关系,但具有一个平面标记结构

我生成了这个SQL,它将结果磨碎,我添加了一些虚拟数据生成,所以它将独立运行

DECLARE @root varchar(250);
SET @root = 'AB&C';

DECLARE @index_base bigint;
SET @index_base = 9999;

DECLARE @tbl TABLE
(
    [FUNCTION] VARCHAR(20)
    ,[SUBFUNCTION]  VARCHAR(20)
    ,[LEVEL] VARCHAR(20)    
    ,[ROLE PROFILE] VARCHAR(20)
);

INSERT INTO @tbl 
VALUES
('Function 1','Sub Function 1','A','RP1')
,('Function 1','Sub Function 1','A','RP2')
,('Function 1','Sub Function 1','A','RP3')
,('Function 1','Sub Function 1','B','RP4')
,('Function 1','Sub Function 1','C','RP5')
,('Function 1','Sub Function 1','C','RP6')
,('Function 1','Sub Function 1','C','RP9')
,('Function 1','Sub Function 1','D','RP10')
,('Function 1','Sub Function 2','A','RP6')
,('Function 1','Sub Function 2','A','RP11')
,('Function 1','Sub Function 2','A','RP12')
,('Function 1','Sub Function 2','A','RP13')
,('Function 1','Sub Function 3','A','RP14')
,('Function 1','Sub Function 3','A','RP15')
,('Function 1','Sub Function 3','B','RP16')
,('Function 1','Sub Function 3','B','RP17')
,('Function 2','Sub Function 1','A','RP1')
,('Function 2','Sub Function 1','B','RP2')
,('Function 2','Sub Function 1','B','RP18')
,('Function 2','Sub Function 1','B','RP19')
,('Function 2','Sub Function 1','C','RP20')
,('Function 2','Sub Function 3','A','RP21')
,('Function 2','Sub Function 3','B','RP22')
,('Function 2','Sub Function 3','B','RP25')
,('Function 2','Sub Function 3','C','RP26')
,('Function 2','Sub Function 3','C','RP30')
,('Function 2','Sub Function 3','C','RP29')
,('Function 2','Sub Function 4','A','RP31')
,('Function 2','Sub Function 4','A','RP33')
,('Function 2','Sub Function 4','A','RP34')
,('Function 2','Sub Function 4','A','RP38')
,('Function 2','Sub Function 4','A','RP41');

DECLARE @tbl2 TABLE
(
ID bigint
,fullname varchar(max)
,parent varchar(max)
,name varchar(250)
)

INSERT INTO @tbl2
SELECT ROW_NUMBER() OVER (ORDER BY fullname) as ID
       ,fullname
       ,parent
       ,name
FROM
(
    SELECT 
           @root as fullname
           ,null as parent
           ,@root as name
    UNION
    SELECT distinct 
           @root + [FUNCTION] as fullname
           ,@root as parent
           ,[FUNCTION] as name
    FROM @tbl
    UNION
    SELECT distinct 
           @root + [FUNCTION] + [SUBFUNCTION] as fullname
           ,@root + [FUNCTION] as parent
           ,[SUBFUNCTION] as name
    FROM @tbl
    UNION
    SELECT distinct 
           @root + [FUNCTION] + [SUBFUNCTION] + [LEVEL] as fullname
           ,@root + [FUNCTION] + [SUBFUNCTION] as parent
           ,[LEVEL]  as name
    FROM @tbl
    UNION
    SELECT distinct
           @root + [FUNCTION] + [SUBFUNCTION] + [LEVEL]  + [ROLE PROFILE] as fullname
           ,@root +[FUNCTION] + [SUBFUNCTION] + [LEVEL]  as parent
           ,[ROLE PROFILE] as name
    FROM @tbl
) a 

SELECT [Id], [Name], [ObjectId], [ParentObjectId]
FROM
(
    SELECT a.name as [Id],  a.name as [Name], CONVERT(varchar(100), @index_base + a.ID) as [ObjectId], CONVERT(varchar(100), @index_base + b.ID) as [ParentObjectId]
    FROM @tbl2 a inner join @tbl2 b ON a.parent = b.fullname
    UNION 
    SELECT @root, @root, CONVERT(varchar(100), @index_base + 1), ''
) n
ORDER BY [ObjectId]
FOR XML PATH('Role')
这将输出所需的XML

注意,以10000开头的ID是必需的,因为根元素有一个空的parentobjectid标记,根元素名为“AB&C”

XML输出如下所示

<Role>
  <Id>AB&amp;C</Id>
  <Name>AB&amp;C</Name>
  <ObjectId>10000</ObjectId>
  <ParentObjectId></ParentObjectId>
</Role>
<Role>
  <Id>Function 1</Id>
  <Name>Function 1</Name>
  <ObjectId>10001</ObjectId>
  <ParentObjectId>10000</ParentObjectId>
</Role>
<Role>
  <Id>Sub Function 1</Id>
  <Name>Sub Function 1</Name>
  <ObjectId>10002</ObjectId>
  <ParentObjectId>10001</ParentObjectId>
</Role>
<Role>
  <Id>A</Id>
  <Name>A</Name>
  <ObjectId>10003</ObjectId>
  <ParentObjectId>10002</ParentObjectId>
</Role>
<Role>
  <Id>RP1</Id>
  <Name>RP1</Name>
  <ObjectId>10004</ObjectId>
  <ParentObjectId>10003</ParentObjectId>
</Role>
<Role>
  <Id>RP2</Id>
  <Name>RP2</Name>
  <ObjectId>10005</ObjectId>
  <ParentObjectId>10003</ParentObjectId>
</Role>
<Role>
  <Id>RP3</Id>
  <Name>RP3</Name>
  <ObjectId>10006</ObjectId>
  <ParentObjectId>10003</ParentObjectId>
</Role>
<Role>
  <Id>B</Id>
  <Name>B</Name>
  <ObjectId>10007</ObjectId>
  <ParentObjectId>10002</ParentObjectId>
</Role>
<Role>
  <Id>RP4</Id>
  <Name>RP4</Name>
  <ObjectId>10008</ObjectId>
  <ParentObjectId>10007</ParentObjectId>
</Role>
<Role>
  <Id>C</Id>
  <Name>C</Name>
  <ObjectId>10009</ObjectId>
  <ParentObjectId>10002</ParentObjectId>
</Role>
<Role>
  <Id>RP5</Id>
  <Name>RP5</Name>
  <ObjectId>10010</ObjectId>
  <ParentObjectId>10009</ParentObjectId>
</Role>
<Role>
  <Id>RP6</Id>
  <Name>RP6</Name>
  <ObjectId>10011</ObjectId>
  <ParentObjectId>10009</ParentObjectId>
</Role>
<Role>
  <Id>RP9</Id>
  <Name>RP9</Name>
  <ObjectId>10012</ObjectId>
  <ParentObjectId>10009</ParentObjectId>
</Role>

AB&;C
AB&;C
10000
职能1
职能1
10001
10000
子职能1
子职能1
10002
10001
A.
A.
10003
10002
RP1
RP1
10004
10003
RP2
RP2
10005
10003
RP3
RP3
10006
10003
B
B
10007
10002
RP4
RP4
10008
10007
C
C
10009
10002
RP5
RP5
10010
10009
RP6
RP6
10011
10009
RP9
RP9
10012
10009

这种方法让人觉得很难看

有没有一种优雅的方法可以得到同样的结果,使整个过程更容易适应。例如,如果引入了一个额外的列和层次结构级别

这段代码是否有效,或者是否有一种方法我应该使用,优雅或其他,以使其性能更好


我目前仅限于使用2008R2(企业版),但对使用最新SQL server功能的任何建议都感兴趣。

这种设计真的很难看…
我真的希望,这是一种尝试,寻找一种新的设计。最简单(也是最好的?)的方法是将其放入与
1:n
相关的表中

XML的结构是-嗯-次优的。你为什么需要这样

这可以按如下方式完成(仅需要@tbl1):

  • 第一个CTE将创建分区列,以便于导航
  • 第二个CTE将找到所有需要的行并找到对象的id
  • 第三个CTE将查找对象的名称及其父id
  • 最后的
    选择将添加根节点
试试看:

WITH Ranked AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION] ORDER BY tbl.[FUNCTION]) AS FunctionRank
          ,ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION],tbl.[SUBFUNCTION] ORDER BY tbl.[SUBFUNCTION]) AS SubfunctionRank
          ,ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION],tbl.[SUBFUNCTION],tbl.[LEVEL] ORDER BY tbl.[LEVEL] ) AS LevelRank
          ,*
    FROM @tbl AS tbl
)
,Unpivoted AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY gr.[FUNCTION],gr.[SUBFUNCTION],gr.[LEVEL],gr.[ROLE PROFILE],gr.RecLevel) + 10000 AS RowInx
          ,*
    FROM
    (
    SELECT 1 AS RecLevel,* FROM Ranked AS r1
    WHERE FunctionRank=1
    UNION ALL
    SELECT 2,* FROM Ranked AS r2
    WHERE SubfunctionRank=1
    UNION ALL
    SELECT 3,* FROM Ranked AS r3
    WHERE LevelRank=1
    UNION ALL
    SELECT 4,* FROM Ranked AS r4
    ) AS gr
)
,Named AS
(
SELECT CASE RecLevel
            WHEN 1 THEN up.[FUNCTION]
            WHEN 2 THEN up.[SUBFUNCTION]
            WHEN 3 THEN up.[LEVEL]
            WHEN 4 THEN up.[ROLE PROFILE]
            ELSE '#Error'
       END AS Name
       ,* 
       ,CASE RecLevel
            WHEN 1 THEN 10000
            WHEN 2 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=1 AND x.RowInx<up.RowInx)
            WHEN 3 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=2 AND x.RowInx<up.RowInx)
            WHEN 4 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=3 AND x.RowInx<up.RowInx)
            ELSE '#Error'
       END AS ParentId
FROM Unpivoted AS up
)
SELECT Id,Name,ObjectId,ParentObjectId
FROM
(
    SELECT 0 AS SortInx
          ,@root AS Id
          ,@root AS Name
          ,10000 AS ObjectId
          ,'' AS ParentObjectId
    UNION ALL
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
          ,n.Name AS Id
          ,n.Name AS Name
          ,n.RowInx AS ObjectId
          ,CAST(n.ParentId AS VARCHAR(MAX)) AS ParentObjectId
    FROM Named AS n
) AS final
ORDER BY final.SortInx
FOR XML PATH('Role')

这个设计真的很难看…
我真的希望,这是一种尝试,寻找一种新的设计。最简单(也是最好的?)的方法是将其放入与
1:n
相关的表中

XML的结构是-嗯-次优的。你为什么需要这样

这可以按如下方式完成(仅需要@tbl1):

  • 第一个CTE将创建分区列,以便于导航
  • 第二个CTE将找到所有需要的行并找到对象的id
  • 第三个CTE将查找对象的名称及其父id
  • 最后的
    选择将添加根节点
试试看:

WITH Ranked AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION] ORDER BY tbl.[FUNCTION]) AS FunctionRank
          ,ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION],tbl.[SUBFUNCTION] ORDER BY tbl.[SUBFUNCTION]) AS SubfunctionRank
          ,ROW_NUMBER() OVER(PARTITION BY tbl.[FUNCTION],tbl.[SUBFUNCTION],tbl.[LEVEL] ORDER BY tbl.[LEVEL] ) AS LevelRank
          ,*
    FROM @tbl AS tbl
)
,Unpivoted AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY gr.[FUNCTION],gr.[SUBFUNCTION],gr.[LEVEL],gr.[ROLE PROFILE],gr.RecLevel) + 10000 AS RowInx
          ,*
    FROM
    (
    SELECT 1 AS RecLevel,* FROM Ranked AS r1
    WHERE FunctionRank=1
    UNION ALL
    SELECT 2,* FROM Ranked AS r2
    WHERE SubfunctionRank=1
    UNION ALL
    SELECT 3,* FROM Ranked AS r3
    WHERE LevelRank=1
    UNION ALL
    SELECT 4,* FROM Ranked AS r4
    ) AS gr
)
,Named AS
(
SELECT CASE RecLevel
            WHEN 1 THEN up.[FUNCTION]
            WHEN 2 THEN up.[SUBFUNCTION]
            WHEN 3 THEN up.[LEVEL]
            WHEN 4 THEN up.[ROLE PROFILE]
            ELSE '#Error'
       END AS Name
       ,* 
       ,CASE RecLevel
            WHEN 1 THEN 10000
            WHEN 2 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=1 AND x.RowInx<up.RowInx)
            WHEN 3 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=2 AND x.RowInx<up.RowInx)
            WHEN 4 THEN (SELECT MAX(x.RowInx) FROM Unpivoted AS x WHERE x.RecLevel=3 AND x.RowInx<up.RowInx)
            ELSE '#Error'
       END AS ParentId
FROM Unpivoted AS up
)
SELECT Id,Name,ObjectId,ParentObjectId
FROM
(
    SELECT 0 AS SortInx
          ,@root AS Id
          ,@root AS Name
          ,10000 AS ObjectId
          ,'' AS ParentObjectId
    UNION ALL
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
          ,n.Name AS Id
          ,n.Name AS Name
          ,n.RowInx AS ObjectId
          ,CAST(n.ParentId AS VARCHAR(MAX)) AS ParentObjectId
    FROM Named AS n
) AS final
ORDER BY final.SortInx
FOR XML PATH('Role')

顺便说一句:这是一个很好的问题(由于这个独立的例子),我这边的预期输出是+1。只是说出来,但是任何通用语言,Java、PHP、Python、VB都可以连接到您的MSSQL数据库来检索一个简单的
SELECT
查询,并用更少的行迭代构建XML文档。SQL最终真的是一种特殊用途的语言。谢谢你的评论,我意识到XML是。。次优就是奉承。有人告诉我,遗留系统需要这种XML格式。源数据由另一个遗留系统提供,如图所示。由于这只用于生成转换后的XML,我不确定是否将其转换为1:tables会使事情变得更简单。这是我知道的第二条注释,但我没有IDE可用,这是一个非常封闭的设置。我不会考虑尝试在VBA中实现它。顺便说一句:这是一个很好的问题(由于独立的示例),我希望得到+1的输出。只是把它放在那里,但是任何通用语言,Java,PHP,Python,VB可以连接到MSSQL数据库,检索一个简单的
SELECT
查询,并以较少的行迭代构建XML文档。SQL最终真的是一种特殊用途的语言。谢谢你的评论,我意识到XML是。。次优就是奉承。有人告诉我,遗留系统需要这种XML格式。源数据由另一个遗留系统提供,如图所示。由于这只用于生成转换后的XML,我不确定是否将其转换为1:tables会使事情变得更简单。这是我知道的第二条注释,但我没有IDE可用,这是一个非常封闭的设置。我不会考虑尝试在VBA中实现它。
    SELECT DENSE_RANK() OVER(ORDER BY tbl.[FUNCTION]) AS FunctionRank
          ,DENSE_RANK() OVER(PARTITION BY tbl.[FUNCTION] ORDER BY tbl.[SUBFUNCTION]) AS SubfunctionRank
          ,DENSE_RANK() OVER(PARTITION BY tbl.[FUNCTION],tbl.[SUBFUNCTION] ORDER BY tbl.[LEVEL] ) AS LevelRank
          ,*
    INTO #Ranked
    FROM @tbl AS tbl;

    SELECT DISTINCT FunctionRank AS FunctionID
                  ,[FUNCTION] 
    INTO #Function
    FROM #Ranked;

    SELECT DISTINCT FunctionRank AS FunctionID
                   ,SubFunctionRank AS SubFunctionID
                   ,[SUBFUNCTION] 
    INTO #SubFunction
    FROM #Ranked;

    SELECT DISTINCT FunctionRank AS FunctionID,SubFunctionRank AS SubFunctionID
                   ,LevelRank AS LevelID
                   ,[LEVEL] 
    INTO #Level
    FROM #Ranked;

    SELECT DISTINCT FunctionRank AS FunctionID,SubFunctionRank AS SubFunctionID,LevelRank AS LevelID
                   ,ROW_NUMBER() OVER(ORDER BY [ROLE PROFILE]) AS RoleProfileID
                   ,[ROLE PROFILE] 
    INTO #RoleProfile
    FROM #Ranked;

    SELECT * FROM #Function;
    SELECT * FROM #SubFunction;
    SELECT * FROM #Level
    SELECT * FROM #RoleProfile;