Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Utwórz zapytanie drzewa z tabeli mapowania numerycznego w SQL (określony format)

Zmodyfikowałem odpowiedź podaną w pierwszym pytaniu...

Najlepiej byłoby, gdyby Twoja tabela przechowywała dane relacji bezpośrednio w indeksowanych kolumnach. Zanim zmienisz strukturę tabeli, możesz spróbować tego:

Tabela z danymi testowymi

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

Spowoduje to przeniesienie potrzebnych danych do nowo utworzonej tabeli tymczasowej o nazwie #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
;

Wynik pośredni

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

A teraz ma miejsce podobne podejście rekurencyjne, jak w mojej pierwszej odpowiedzi. Ale - ponieważ używa teraz prawdziwego stołu, a całe dzielenie ciągów już miało miejsce - powinno być szybsze...

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;

I wreszcie sprzątam

DROP TABLE #tempHierarchy;

A oto wynik końcowy

+-----------+--------------+----+----------+----------------+----------+
| 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server Konwertuj Varchar na Datetime

  2. Implementacja obsługi błędów i transakcji w SQL Server

  3. Wyciek pamięci w aplikacji .net

  4. Co dzieje się z niezatwierdzoną transakcją po zamknięciu połączenia?

  5. Podstawy i użycie podpowiedzi NOLOCK w SQL Server