从 SQL 中的数值映射 Table 创建树查询(特定格式)

Create Tree Query From Numeric Mapping Table in SQL (Specific Format)

我有一个从会计软件导出的 table,如下所示。

 AccountID     AccountName    
  ---------     -----------
  11            Acc11
  12            Acc12
  13            Acc13
  11/11         Acc11/11
  11/12         Acc11/12
  11/111        Acc11/111
  11/11/001     Acc11/11/001
  11/11/002     Acc11/11/002
  12/111        Acc12/111
  12/112        Acc12/112

我想在 MS-SQL Server 2008 中将其转换为树查询,以便在我的 win 应用程序中将其用作 Treelist 数据源。 我之前提出过这个问题,它的回答方式对于我有超过 5000 条记录的大 table () 来说非常非常慢。 但我认为计算“/”并用“/”分隔 AccountID 字段可以更轻松、更快地解决我的问题。 无论如何,我的预期结果必须如下所示:

   AccountID     AccountName    ID   ParentID  Level   HasChild
   ---------     -----------    ---  --------- ------  --------
   11            Acc11          1     Null       1        1
   12            Acc12          2     Null       1        1
   13            Acc13          3     Null       1        0
   11/11         Acc11/11       4     1          2        1
   11/12         Acc11/12       5     1          2        0
   11/111        Acc11/111      6     1          2        0
   11/11/001     Acc11/11/001   7     4          3        0
   11/11/002     Acc11/11/002   8     4          3        0
   12/111        Acc12/111      9     2          2        0
   12/112        Acc12/112      10    2          2        0

请帮助我。

我修改了第一个问题的答案...

如果您的 table 将关系数据直接保存在索引列中,那将是最好的。在更改 table 的结构之前,您可以尝试这样做:

A table 带测试数据

DECLARE @tbl TABLE ( AccountID  VARCHAR(100), AccountName VARCHAR(100));
INSERT INTO @tbl VALUES 
 ('11','Acc11')
,('12','Acc12')
,('13','Acc13')
,('11/11','Acc11/11')
,('11/12','Acc11/12')
,('11/111','Acc11/111')
,('11/11/001','Acc11/11/001')
,('11/11/002','Acc11/11/002')
,('12/111','Acc12/111')
,('12/112','Acc12/112');

这会将所需数据放入新创建的名为 #tempHierarchy

的临时文件 table
SELECT AccountID
      ,AccountName
      ,ROW_NUMBER() OVER(ORDER BY LEN(AccountID)-LEN(REPLACE(AccountID,'/','')),AccountID) AS ID
      ,Extended.HierarchyLevel
      ,STUFF(
       (
         SELECT '/' + A.B.value('.','varchar(10)')
         FROM Extended.IDsXML.nodes('/x[position() <= sql:column("HierarchyLevel")]') AS A(B)
         FOR XML PATH('')
       ),1,2,'') AS ParentPath
      ,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")+1][1]','varchar(10)') AS ownID
      ,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")][1]','varchar(10)') AS ancestorID
INTO #tempHierarchy
FROM @tbl
CROSS APPLY(SELECT LEN(AccountID)-LEN(REPLACE(AccountID,'/','')) + 1 AS HierarchyLevel
                  ,CAST('<x></x><x>' + REPLACE(AccountID,'/','</x><x>') + '</x>' AS XML) AS IDsXML) AS Extended
;

中间结果

+-----------+--------------+----+----------------+------------+-------+------------+
| AccountID | AccountName  | ID | HierarchyLevel | ParentPath | ownID | ancestorID |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11        | Acc11        | 1  | 1              |            | 11    |            |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12        | Acc12        | 2  | 1              |            | 12    |            |
+-----------+--------------+----+----------------+------------+-------+------------+
| 13        | Acc13        | 3  | 1              |            | 13    |            |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11     | Acc11/11     | 4  | 2              | 11         | 11    | 11         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/111    | Acc11/111    | 5  | 2              | 11         | 111   | 11         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/12     | Acc11/12     | 6  | 2              | 11         | 12    | 11         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/111    | Acc12/111    | 7  | 2              | 12         | 111   | 12         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/112    | Acc12/112    | 8  | 2              | 12         | 112   | 12         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/001 | Acc11/11/001 | 9  | 3              | 11/11      | 001   | 11         |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/002 | Acc11/11/002 | 10 | 3              | 11/11      | 002   | 11         |
+-----------+--------------+----+----------------+------------+-------+------------+

现在出现了与我的第一个答案类似的递归方法。但是 - 因为它现在使用的是真正的 table 并且所有的字符串拆分都已经发生 - 它应该更快......

WITH RecursiveCTE AS
(
    SELECT th.*
           ,CAST(NULL AS BIGINT) AS ParentID 
           ,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=th.AccountID) THEN 1 ELSE 0 END AS HasChild
    FROM #tempHierarchy AS th WHERE th.HierarchyLevel=1
    UNION ALL
    SELECT sa.AccountID
          ,sa.AccountName
          ,sa.ID
          ,sa.HierarchyLevel
          ,sa.ParentPath
          ,sa.ownID
          ,sa.ancestorID
          ,(SELECT x.ID FROM #tempHierarchy AS x WHERE x.AccountID=sa.ParentPath)
          ,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=sa.AccountID) THEN 1 ELSE 0 END AS HasChild
    FROM RecursiveCTE AS r
    INNER JOIN #tempHierarchy AS sa ON sa.HierarchyLevel=r.HierarchyLevel+1 
                                       AND r.AccountID=sa.ParentPath
)
SELECT r.AccountID
      ,r.AccountName
      ,r.ID
      ,r.ParentID
      ,r.HierarchyLevel
      ,r.HasChild
FROM RecursiveCTE AS r
ORDER BY HierarchyLevel,ParentID;

最后我清理了

DROP TABLE #tempHierarchy;

这是最终结果

+-----------+--------------+----+----------+----------------+----------+
| AccountID | AccountName  | ID | ParentID | HierarchyLevel | HasChild |
+-----------+--------------+----+----------+----------------+----------+
| 11        | Acc11        | 1  | NULL     | 1              | 1        |
+-----------+--------------+----+----------+----------------+----------+
| 12        | Acc12        | 2  | NULL     | 1              | 1        |
+-----------+--------------+----+----------+----------------+----------+
| 13        | Acc13        | 3  | NULL     | 1              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 11/11     | Acc11/11     | 4  | 1        | 2              | 1        |
+-----------+--------------+----+----------+----------------+----------+
| 11/111    | Acc11/111    | 5  | 1        | 2              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 11/12     | Acc11/12     | 6  | 1        | 2              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 12/111    | Acc12/111    | 7  | 2        | 2              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 12/112    | Acc12/112    | 8  | 2        | 2              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/001 | Acc11/11/001 | 9  | 4        | 3              | 0        |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/002 | Acc11/11/002 | 10 | 4        | 3              | 0        |
+-----------+--------------+----+----------+----------------+----------+