В SQL Server (2016+) таким образом я бы достиг вышеуказанного набора результатов:
USE Sandbox;
GO
WITH VTE AS(
SELECT *
FROM (VALUES (1 ,0 ),
(2 ,1 ),
(3 ,1 ),
(4 ,1 ),
(6 ,2 ),
(7 ,2 ),
(8 ,3 ),
(9 ,8 ),
(10,0 ),
(11,10),
(12,10),
(13,12)) V(ID, ID_ref)),
CTE AS (
SELECT ID,
CONVERT(varchar(30),CONVERT(varchar(4),ID)) AS Delimited
FROM VTE V
WHERE V.ID_ref = 0
UNION ALL
SELECT V.ID,
CONVERT(varchar(30),CONCAT(C.Delimited,',' + CONVERT(varchar(4),V.ID)))
FROM CTE C
JOIN VTE V ON V.ID_ref = C.ID),
Splits AS(
SELECT C.ID,
SS.value
FROM CTE C
CROSS APPLY STRING_SPLIT(C.Delimited,',') SS)
SELECT V.ID,
COUNT(S.ID) - 1 AS [Count]
FROM VTE V
JOIN Splits S ON S.[value] = V.ID
GROUP BY V.ID;
Сначала создается список с разделителями для каждого идентификатора на каждом уровне.Затем он разделяет их и, наконец, выполняет подсчет -1.
. Если вы не используете SQL Server 2016+, вы можете использовать разделитель XML или delimitedsplit8k(_lead)
.
* 1009.* Обратите внимание, что rCTe перестанет повторяться при 100 циклах.Вам нужно будет использовать OPTION (MAXRECURSION N)
, чтобы увеличить количество циклов (где N
- подходящее число максимального слоя, который у вас может быть).