Согласно комментарию Кевина, ответ заключается в том, чтобы включить флаг, указывающий, что данный узел является допустимой начальной точкой, и может быть включен в запрос:
В этом случае 'A' и «X» задаются в качестве начальных точек, поэтому мы будем отмечать все записи, в которых инициатором является «A» или «X»:
CREATE TABLE #tblLoop
(
person1 varchar(20),
person2 varchar(20),
ColDate date,
isRoot INT
);
INSERT INTO #tblLoop VALUES('A','B','2020-01-01',1),
('A','C','2020-01-01',1),
('A','D','2020-01-01',1),
('B','E','2020-01-02',0),
('B','F','2020-01-02',0),
('D','G','2020-01-03',0),
('D','H','2020-01-03',0),
('F','i','2020-01-04',0),
('G','J','2020-01-05',0),
('i','A','2020-01-06',0),
('J','D','2020-01-07',0),
('X','Y','2020-01-08',1),
('X','Z','2020-01-08',1),
('Z','X','2020-01-09',0),
('Y','W','2020-01-09',0);
. Затем следующий запрос может быть изменен следующим образом:
;WITH CTE AS
(
SELECT Person1, Person2, isRoot,
CONVERT(VARCHAR(MAX), (','+ Person1+ ','+ Person2+ ',')) AS nodes, 1 AS lev,
(CASE WHEN Person1 = Person2 THEN 1 ELSE 0 END) AS has_cycle
FROM #tblLoop e
UNION ALL
SELECT cte.Person1, e.Person2, cte.isRoot,
CONVERT(VARCHAR(MAX), (cte.nodes+ e.Person2+ ',')), lev + 1,
(CASE WHEN cte.nodes LIKE ('%,'+ e.Person2+ ',%') THEN 1 ELSE 0 END) AS has_cycle
FROM CTE
JOIN #tblLoop e ON e.Person1 = cte.Person2
WHERE cte.has_cycle = 0
)
SELECT *
FROM CTE
WHERE has_cycle = 1
AND isRoot = 1
Благодарим Кевина за идею, это всего лишь рабочая реализация.