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

Opret træforespørgsel fra numerisk kortlægningstabel i SQL (specifikt format)

Jeg ændrede mit svar i det første spørgsmål...

Det ville være bedst, hvis din tabel ville holde relationsdataene direkte i indekserede kolonner. Før du ændrer din tabels struktur, kan du prøve dette:

En tabel med testdata

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

Dette vil få de nødvendige data ind i en nyoprettet midlertidig tabel kaldet #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
;
 

Mellemresultatet

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

Og nu finder en lignende rekursiv tilgang sted som i mit første svar. Men - da den bruger en rigtig tabel nu, og al strengopdeling allerede har fundet sted - burde det være hurtigere...

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;
 

Og til sidst rydder jeg op

DROP TABLE #tempHierarchy;
 

Og her er det endelige resultat

+-----------+--------------+----+----------+----------------+----------+ | 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. Sådan får du en liste over aktiverede/deaktiverede tjekbegrænsninger i SQL Server-databasen - SQL Server / TSQL-vejledning, del 86

  2. Brug af Count til at finde antallet af forekomster

  3. Dvale, hvordan man modellerer dette forhold

  4. PostgreSQL drop-rolle mislykkes på grund af standardprivilegier