Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Crea query ad albero dalla tabella di mappatura numerica in SQL (formato specifico)

Ho modificato la mia risposta data nella prima domanda...

Sarebbe meglio se la tua tabella conservasse i dati della relazione direttamente nelle colonne indicizzate. Prima di modificare la struttura della tua tabella potresti provare questo:

Una tabella con i dati del test

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

Questo otterrà i dati necessari in una tabella temporanea appena creata chiamata #tempHierarchy

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
;

Il risultato intermedio

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

E ora si verifica un approccio ricorsivo simile come nella mia prima risposta. Ma - poiché ora sta usando una tabella reale e tutta la divisione delle stringhe è già avvenuta - dovrebbe essere più veloce...

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;

E finalmente pulisco

DROP TABLE #tempHierarchy;

Ed ecco il risultato finale

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