Найти петли в данных с помощью SQL Server - PullRequest
1 голос
/ 06 февраля 2020

У меня есть следующие данные, чтобы найти таблицу l oop.

:

CREATE TABLE tblLoop
(
    person1 varchar(20),
    person2 varchar(20)
);

INSERT INTO tblLoop VALUES('A','B'),('A','C'),('A','D'),
                          ('B','E'),('B','F'),
                          ('D','G'),('D','H'),
                          ('F','i'),
                          ('G','J'),
                          ('i','A'),
                          ('J','D');

Редактировать: Добавлено еще несколько значений

INSERT INTO tblLoop VALUES('X','Y'),('X','Z'),('Z','X'),('Y','W');

Просмотр записей например: enter image description here enter image description here

Примечание : существует возможность множества деревьев, как указано выше, нам нужно найти все деревья l oop данные.

Требование: мне нужно найти лиц, которые составляют oop. Для примера в приведенных данных мы нашли 2 L oop:

L oop 1: A, связанных с B, связанных с F, связанных с i, связанных с A.

L oop 2: A, связанный с D, связанный с G, связанный с J, связанный с D.

Ожидаемый результат:

LoopFound
--------------------
A->B->F->i->A
A->D->G->J->D
X->Z->X

Моя попытка:

;WITH CTE_Loop AS 
(
    SELECT  t.Person1,t.Person2,
            CONVERT(VARCHAR(500), t.Person1 + '->' + t.Person2) AS [Loop],
            0 AS FoundFlag
    FROM tblLoop t 
    UNION ALL
    SELECT t.Person1,t.Person2,
            CONVERT(VARCHAR(500), cte.[Loop] +'->'+t.Person2) AS [Loop],
            CASE WHEN CHARINDEX(t.Person2, cte.[Loop]) != 0 THEN 1 ELSE 0 END AS FoundFlag
    FROM CTE_Loop cte
    INNER JOIN tblLoop t ON t.Person1 = cte.Person2
    WHERE cte.FoundFlag = 0  AND t.Person1 <> '-' AND t.Person2 <> '-'
)
SELECT [Loop] AS LoopFound
FROM CTE_Loop 
WHERE FoundFlag = 1 
GROUP BY [Loop];

Ответы [ 2 ]

0 голосов
/ 06 февраля 2020

Вы можете получить начало и конец циклов, предполагая иерархию некоторого вида (например, A предшествует B, который находится перед C et c), и исключить строки, у которых person2 не отображается как person1 (то есть концы отношение / цепочка) ...

--get loops
select  distinct a.person1, b.person1, b.person2  --a.person1, b.person1 :: loop range(start-end)
from tblLoop as a
join tblLoop as b on a.person1 = b.person2 and a.person1 <= b.person1 --assume a hierarchy by name (A is on a higher/or equal level than B etc)
where exists(select * from tblLoop as d where d.person1 = a.person2) --exclude dead ends (person2 which is not person1);

.. и введите это в cte. Вы можете работать с перекрытиями и циклами без одинакового начала и конца.

(например, A-> D-> G-> J-> D или D-> G-> J-> D - это все oop?)

;WITH CTE_Loop AS 
(
    SELECT  t.Person1,t.Person2,
            CONVERT(VARCHAR(500), t.Person1 + '->' + t.Person2) AS [Loop],
            0 AS FoundFlag
    FROM tblLoop t 
    where t.person1 in 
    (
        select  distinct a.person1--, b.person1, b.person2  --a.person1, b.person1 :: loop range(start-end)
        from tblLoop as a
        join tblLoop as b on a.person1 = b.person2 and a.person1 <= b.person1 --assume a hierarchy by name (A is on a higher/or equal level than B etc)
        where exists(select * from tblLoop as d where d.person1 = a.person2) --exclude dead ends (person2 which is not person1)     
    )
    UNION ALL
    SELECT t.Person1,t.Person2,
            CONVERT(VARCHAR(500), cte.[Loop] +'->'+t.Person2) AS [Loop],
            CASE WHEN CHARINDEX(t.Person2, cte.[Loop]) != 0 THEN 1 ELSE 0 END AS FoundFlag
    FROM CTE_Loop cte
    INNER JOIN tblLoop t ON t.Person1 = cte.Person2
    WHERE cte.FoundFlag = 0  AND t.Person1 <> '-' AND t.Person2 <> '-'
)
SELECT [Loop] AS LoopFound
FROM CTE_Loop 
WHERE FoundFlag = 1 
GROUP BY [Loop];
0 голосов
/ 06 февраля 2020
SELECT  
   CONCAT_WS('->',t1.person1,t1.person2,t2.person2 ,t3.person2,t4.person2) 
FROM tblLoop t1
left join tblLoop t2 on t1.person2 = t2.person1
left join tblLoop t3 on t2.person2 = t3.person1
left join tblLoop t4 on t3.person2 = t4.person1
where t1.person1 = 'A'AND t4.person2 IS NOT NULL
...