1

Я просто пытаюсь понять CTE и рекурсию, чтобы решить проблему, к которой я ранее использовал бы курсор.SQL Рекурсивный CTE: поиск объектов, связанных свойством

create table ##ACC (
AccNo int, 
Property char 
) 

Insert into ##ACC 
VALUES (1,'A'),(1,'B'),(2,'A'),(2,'C'),(3,'C'),(4,'D') 

То, что я пытаюсь добиться, чтобы получить список всех AccNo, и все AccNo-х они связаны с помощью собственности. Так что мои ожидаемые результаты

PrimaryAccNo | LinkedAccNo 
1 | 1 
1 | 2 
1 | 3 
2 | 1 
2 | 2 
2 | 3 
3 | 1 
3 | 2 
3 | 3 
4 | 4 

я попытался следующий код и вариации, но я либо получить 4 результат (PrimaryAccNo = LinkedAccNo) или только я ударил 100 рекурсии.

WITH Groups(PrimaryAccNo, LinkedAccNo) 
AS 
(
Select distinct AccNo, AccNo from ##ACC 

UNION ALL 
Select g.PrimaryAccNo, p.AccNo from 
##ACC p inner join Groups g on p.AccNo=g.LinkedAccNo 
inner join ##ACC pp on p.Property=pp.Property 
where p.AccNo<> pp.AccNo 
) 
Select PrimaryAccNo,LinkedAccNo 
from Groups 

Что я делаю неправильно?

+1

Отладочный наконечник: добавьте '0 в качестве глубины' к якорю в CTE и' Depth + 1' к рекурсивному 'select'. Вы можете остановить рекурсию в любой момент, добавив к рекурсивному предложению 'where', например. 'и Depth <42', и посмотреть, что происходит. – HABO

+0

@HABO спасибо. Мне придется посмотреть на это. Прямо сейчас я не вижу, как это помогает мне. – GavinP

+0

Я посмотрел. К сожалению, я не знаю, сколько рекурсий мне нужно сделать для моего набора данных, поэтому в приведенном выше примере я могу сделать его быстрым и быстрым с помощью 'Depth <2', но на моем реальном наборе данных мой запрос был запущен для 13 минут теперь с 'Depth <4' .... Проблемы, похоже, связаны с добавлением дубликатов, а не только с уникальными новыми совпадениями. Я думаю, что я мог бы просто вернуться к подходу, основанному на курсорах, с которым я бы пошел. – GavinP

ответ

0

Вы используете бесконечный цикл, вызванный циклами внутри ваших данных, например: 1> 2> 3> 2> .... Решение состоит в том, чтобы отслеживать строки, которые уже были «потреблены». Из-за ограничений в CTE это необходимо сделать, включив историю в каждую строку CTE, например. путем сборки пути, следующего за каждой строкой. Вы можете раскомментировать , Path в окончательном select, чтобы узнать, что происходит.

-- Sample data. 
declare @ACC as Table (AccNo Int, Property Char); 
insert into @ACC values 
    (1, 'A'), (1, 'B'), (2, 'A'), (2, 'C'), (3, 'C'), (4, 'D'); 
select * from @ACC; 

-- Recursive CTE. 
with Groups as (
    select distinct AccNo, AccNo as LinkedAccNo, 
    Cast('|' + Cast(AccNo as VarChar(10)) + '|' as VarChar(1024)) as Path 
    from @ACC 
    union all 
    select G.AccNo, A.AccNo, Cast(Path + Cast(A.AccNo as VarChar(10)) + '|' as VarChar(1024)) 
    from Groups as G inner join -- Take the latest round of new rows ... 
     @ACC as AP on AP.AccNo = G.LinkedAccNo inner join -- ... and get the Property for each ... 
     @ACC as A on A.Property = AP.Property -- ... to find new linked rows. 
     where G.Path not like '%|' + Cast(A.AccNo as VarChar(10)) + '|%') 
    select AccNo, LinkedAccNo -- , Path 
    from Groups 
    order by AccNo, LinkedAccNo;