sql >> Database >  >> RDS >> Sqlserver

Tree-query maken van numerieke toewijzingstabel in SQL (specifieke indeling)

Ik heb mijn antwoord in de eerste vraag aangepast...

Het zou het beste zijn als uw tabel de relatiegegevens direct in geïndexeerde kolommen zou houden. Voordat u de structuur van uw tabel wijzigt, kunt u dit proberen:

Een tabel met testgegevens

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

Dit zal de benodigde gegevens in een nieuw gemaakte tijdelijke tabel krijgen met de naam #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
;

Het tussenresultaat

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

En nu vindt een vergelijkbare recursieve benadering plaats als in mijn eerste antwoord. Maar - aangezien het nu een echte tabel gebruikt en alle stringsplitsing al heeft plaatsgevonden - zou het sneller moeten zijn...

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;

En tot slot ruim ik op

DROP TABLE #tempHierarchy;

En hier is het eindresultaat

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



  1. Evenementenplanner in PostgreSQL?

  2. Hoe voeg ik AUTO_INCREMENT toe aan een bestaande kolom?

  3. EXISTS gebruiken met MySQL

  4. Hoe kan ik de tijdzoneregio ophalen van SQLPLUS?