Как сказал Гордон, вам нужен рекурсивный CTE, который воплощает определенный тип рекурсии с фиксированной точкой. Я думаю, что проще всего думать об этом в два этапа:
- Завершить переходное замыкание;затем
- Используйте это для получения желаемого результата.
Мы будем использовать CTE для первой части. У CTE есть два предложения, разделенных «объединением всех». Первый пункт запускается один раз, чтобы заполнить их насосом;второй выполняется многократно, пока не выдаст выходных данных (или не превысит допустимое количество итераций). Каждый раз, когда выполняется второе предложение, происходят две вещи:
- Результаты добавляются к результатам CTE;и
- Результаты заменяют «рабочее» значение CTE в CTE.
Имея это в виду, вот CTE, которое вычисляет переходное замыканиезапрос. Он делает несколько важных предположений:
- В ваших цепочках идентификаторов нет циклов;
- Идентификаторы постоянно увеличиваются;и
- Даты не имеют значения.
Вот код:
with cte as (
select from_id, to_id
from t
union all
select t1.from_id, t2.to_id
from cte t1 join t t2 on t1.to_id = t2.from_id
)
select * from cte;
Первое предложение сгенерирует вашу исходную таблицу (без столбца даты):
Round 1:
FROM_ID TO_ID
------- -------
1001 2001
1002 2002
1003 2003
2001 3001
2002 3002
3001 4001
Тогда второе предложение будет использовать этот результат в качестве рабочей таблицы и объединит ее с исходной таблицей. Это приведет к следующему раунду:
Round 2:
FROM_ID TO_ID
------- -------
1001 3001
1002 3002
2001 4001
Это будет добавлено к результату, но станет рабочим столом для следующего раунда. Таким образом, наш третий раунд дает нам:
Round 3:
FROM_ID TO_ID
------- -------
1001 4001
Следующий раунд не дает результатов, что означает, что ни один раунд не даст никаких новых результатов - CTE достиг фиксированной точки. Это когда CTE завершает работу и дает нам свой окончательный результат:
FROM_ID TO_ID
------- -------
1001 2001
1002 2002
1003 2003
2001 3001
2002 3002
3001 4001
1001 3001
1002 3002
2001 4001
1001 4001
Нам все еще нужно выполнить шаг 2, но здесь все относительно просто: ваш результат - это просто набор строк в CTE смаксимальный TO_ID для каждого FROM_ID. Мы добавим небольшую постобработку в CTE:
with cte as (
select from_id, to_id
from t
union all
select t1.from_id, t2.to_id
from cte t1 join t t2 on t1.to_id = t2.from_id
)
select from_id, max(to_id) as to_id
from cte
group by from_id;
, что даст нам:
FROM_ID TO_ID
------- -------
1001 4001
1002 3002
1003 2003
2001 4001
2002 3002
3001 4001
Вот и все. Как отметил Гордон, другой вопрос также должен быть простым с использованием результатов того же CTE:
with cte as (
select from_id, to_id
from t
union all
select t1.from_id, t2.to_id
from cte t1 join t t2 on t1.to_id = t2.from_id
)
select from_id id_1, to_id id_1
from cte
union all
select to_id id_1, from_id id_1
from cte;