SQL-запрос для топологической сортировки

У меня есть ориентированный ациклический граф:

DROP TABLE IF EXISTS #Edges
CREATE TABLE #Edges(from_node int, to_node int);
INSERT INTO #Edges VALUES (1,2),(1,3),(1,4),(5,1);

введите здесь описание изображения

Я хочу перечислить все узлы, всегда перечисляя узел до его исходного узла. Например: 2, 3, 4, 1, 5.

Его также называют топологическим упорядочением. Как это можно сделать в SQL?


person Ludovic Aubert    schedule 02.12.2019    source источник
comment
Какие СУБД вы используете?   -  person jarlh    schedule 02.12.2019


Ответы (2)


Вы можете использовать рекурсивный CTE для вычисления глубины. Затем упорядочить по глубине:

with cte as (
      select e.from_node, e.to_node, 1 as lev
      from edges e
      where not exists (select 1 from edges e2 where e2.to_node = e.from_node)
      union all
      select e.from_node, e.to_node, lev + 1
      from cte join
           edges e
           on e.from_node = cte.to_node
     )
select *
from cte
order by lev desc;

РЕДАКТИРОВАТЬ:

Я заметил, что у вас нет «1» в списке ребер. Чтобы справиться с этим:

with cte as (
      select 1 as from_node, e.from_node as to_node, 1 as lev
      from edges e
      where not exists (select 1 from edges e2 where e2.to_node = e.from_node)
      union all
      select e.from_node, e.to_node, lev + 1
      from cte join
           edges e
           on e.from_node = cte.to_node
      -- where lev < 5
     )
select *
from cte
order by lev desc;

Вот скрипт db‹>.

person Gordon Linoff    schedule 02.12.2019

DROP TABLE IF EXISTS #topological_sorted
CREATE TABLE #topological_sorted(id int identity(1,1) primary key, n int);


WITH rcte(n) AS (

    SELECT e1.to_node
    FROM #Edges AS e1
    LEFT JOIN #Edges AS e2 ON e1.to_node = e2.from_node 
    WHERE e2.from_node IS NULL

    UNION ALL

    SELECT e.from_node
    FROM #Edges AS e 
    JOIN rcte ON e.to_node = rcte.n

)
INSERT INTO #topological_sorted(n)
SELECT *
FROM rcte;

SELECT * FROM #topological_sorted

введите здесь описание изображения

узлы могут быть перечислены несколько раз. Мы хотим сохранить только первое вхождение:

DROP TABLE IF EXISTS #topological_sorted_2

SELECT *, MIN(id) OVER (PARTITION BY n) AS idm
INTO #topological_sorted_2
FROM #topological_sorted 
ORDER BY id;

SELECT * FROM #topological_sorted_2
WHERE id=idm
ORDER BY id;

введите здесь описание изображения

person Ludovic Aubert    schedule 02.12.2019