问题
I have an exported table from my accounting software like below in SQL Server 2008.
AccountID AccountName SourceCast
--------- ----------- -----------
11 Acc11 9401
12 Acc12 9401
13 Acc13 9401
11/11 Acc11/11 9401
11/12 Acc11/12 9401
11/111 Acc11/111 9401
11/11/001 Acc11/11/001 9401
11/11/002 Acc11/11/002 9401
15 Acc15 9402
12 Acc12 9402
13 Acc13 9402
15/15 Acc15/15 9402
15/12 Acc15/12 9402
15/111 Acc15/111 9402
15/15/001 Acc15/15/001 9402
15/15/002 Acc15/15/002 9402
Then expected result must be like below:
AccountID AccountName ID ParentID Level HasChild SourceCast
--------- ----------- --- --------- ------ -------- ----------
9401 9401 1 Null 1 1 9401
11 Acc11 2 1 2 1 9401
12 Acc12 3 1 2 1 9401
13 Acc13 4 1 2 0 9401
11/11 Acc11/11 5 2 3 1 9401
11/12 Acc11/12 6 2 3 0 9401
11/111 Acc11/111 7 2 3 0 9401
11/11/001 Acc11/11/001 8 5 4 0 9401
11/11/002 Acc11/11/002 9 5 4 0 9401
12/111 Acc12/111 10 5 3 0 9401
12/112 Acc12/112 11 5 3 0 9401
9402 9402 12 Null 1 1 9401
15 Acc15 13 12 2 1 9402
12 Acc12 14 12 2 1 9402
13 Acc13 15 12 2 0 9402
15/15 Acc15/15 16 13 2 1 9402
15/12 Acc15/12 17 13 2 0 9402
15/111 Acc15/111 18 13 3 0 9402
15/15/001 Acc15/15/001 19 16 4 0 9402
15/15/002 Acc15/15/002 20 16 4 0 9402
12/111 Acc12/111 21 14 3 0 9402
12/112 Acc12/112 22 14 3 0 9402
This question raised before without "SourceCast" field in this Link and answered by Shnugo. My main table is very large and it has more than 30000 records.
Would you please help me?
Thanks.
You can find my sample table here:
CREATE TABLE Account(AccountID NVARCHAR(50) NOT NULL PRIMARY KEY
,AccountName NVARCHAR(50) NOT NULL
,SourceCast INTEGER NOT NULL);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11',N'Acc11',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'12',N'Acc12',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'13',N'Acc13',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11',N'Acc11/11',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/12',N'Acc11/12',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/111',N'Acc11/111',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11/001',N'Acc11/11/001',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11/002',N'Acc11/11/002',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15',N'Acc15',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'12',N'Acc12',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'13',N'Acc13',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15',N'Acc15/15',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/12',N'Acc15/12',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/111',N'Acc15/111',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15/001',N'Acc15/15/001',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15/002',N'Acc15/15/002',9402);
回答1:
This should get you pretty close... But I must admit, that I do not fully understand the logic... Why is there at 9401/Acc12 the "HasChild" set to 1 ?
Try it with this. The numbering is partitioned by the SourceCast. That means, that both sets (or many sets are treated as one set each, depending on the SourceCast. Additionally there is a running ID, like in your example:
CREATE TABLE Account(AccountID NVARCHAR(50) NOT NULL
,AccountName NVARCHAR(50) NOT NULL
,SourceCast INTEGER NOT NULL
,PRIMARY KEY (AccountID,SourceCast));
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11',N'Acc11',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'12',N'Acc12',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'13',N'Acc13',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11',N'Acc11/11',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/12',N'Acc11/12',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/111',N'Acc11/111',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11/001',N'Acc11/11/001',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'11/11/002',N'Acc11/11/002',9401);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15',N'Acc15',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'12',N'Acc12',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'13',N'Acc13',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15',N'Acc15/15',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/12',N'Acc15/12',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/111',N'Acc15/111',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15/001',N'Acc15/15/001',9402);
INSERT INTO Account(AccountID,AccountName,SourceCast) VALUES (N'15/15/002',N'Acc15/15/002',9402);
GO
WITH DistinctSourceCasts AS (SELECT DISTINCT SourceCast FROM Account)
SELECT *
INTO #tempHierarchy
FROM
(
SELECT SourceCast
,CAST(SourceCast AS NVARCHAR(50)) AS AccountID
,CAST(SourceCast AS NVARCHAR(50)) AS AccountName
,0 AS ID
,0 AS HierarchyLevel
,'' AS ParentPath
,'' AS ownID
,'' AS ancestorID
FROM DistinctSourceCasts
UNION ALL
SELECT SourceCast
,AccountID
,AccountName
,ROW_NUMBER() OVER(PARTITION BY SourceCast 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
FROM Account
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
) AS tbl;
UPDATE #tempHierarchy SET ParentPath = SourceCast,ancestorID=SourceCast WHERE HierarchyLevel=1;
UPDATE #tempHierarchy SET ownID=SourceCast WHERE HierarchyLevel=0;
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=0
UNION ALL
SELECT r.SourceCast
,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
AND r.SourceCast=sa.SourceCast
)
SELECT SourceCast
,r.AccountID
,r.AccountName
,ROW_NUMBER() OVER(ORDER BY SourceCast,HierarchyLevel,ParentID,ID) AS ID
,r.ID AS GroupedID
,r.ParentID
,r.HierarchyLevel
,r.HasChild
FROM RecursiveCTE AS r
DROP TABLE #tempHierarchy;
The result:
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| SourceCast | AccountID | AccountName | ID | GroupedID | ParentID | HierarchyLevel | HasChild |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 9401 | 9401 | 1 | 0 | NULL | 0 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11 | Acc11 | 2 | 1 | 0 | 1 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 12 | Acc12 | 3 | 2 | 0 | 1 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 13 | Acc13 | 4 | 3 | 0 | 1 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11/11 | Acc11/11 | 5 | 4 | 1 | 2 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11/111 | Acc11/111 | 6 | 5 | 1 | 2 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11/12 | Acc11/12 | 7 | 6 | 1 | 2 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11/11/001 | Acc11/11/001 | 8 | 7 | 4 | 3 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9401 | 11/11/002 | Acc11/11/002 | 9 | 8 | 4 | 3 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 9402 | 9402 | 10 | 0 | NULL | 0 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 12 | Acc12 | 11 | 1 | 0 | 1 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 13 | Acc13 | 12 | 2 | 0 | 1 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15 | Acc15 | 13 | 3 | 0 | 1 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15/111 | Acc15/111 | 14 | 4 | 3 | 2 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15/12 | Acc15/12 | 15 | 5 | 3 | 2 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15/15 | Acc15/15 | 16 | 6 | 3 | 2 | 1 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15/15/001 | Acc15/15/001 | 17 | 7 | 6 | 3 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
| 9402 | 15/15/002 | Acc15/15/002 | 18 | 8 | 6 | 3 | 0 |
+------------+-----------+--------------+----+-----------+----------+----------------+----------+
来源:https://stackoverflow.com/questions/36948058/create-tree-query-from-numeric-mapping-table-in-sql-with-seperator-field