3

Я новичок в WITH RECURSIVE в PostgreSQL. У меня есть достаточно стандартный рекурсивный запрос, следующий за списком смежности. Если у меня есть, например:С запросом RECURSIVE, чтобы выбрать самые длинные пути

1 -> 2 
2 -> 3 
3 -> 4 
3 -> 5 
5 -> 6 

производит:

1 
1,2 
1,2,3 
1,2,3,4 
1,2,3,5 
1,2,3,5,6 

То, что я хотел бы, чтобы иметь только:

1,2,3,4 
1,2,3,5,6 

Но я не могу видеть, как это сделать в Postgres. Казалось бы, это «выбрать самые длинные пути» или «выбрать пути, которые не содержатся в другом пути». Вероятно, я могу увидеть, как это сделать с присоединением к себе, но это кажется довольно неэффективным.

Пример запроса:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
    SELECT g.id, g.link, g.data, 1, ARRAY[g.id], false 
    FROM graph g 
    UNION ALL 
    SELECT g.id, g.link, g.data, sg.depth + 1, path || g.id, g.id = ANY(path) 
    FROM graph g, search_graph sg 
    WHERE g.id = sg.link AND NOT cycle 
) 
SELECT * FROM search_graph; 
+1

Поиск номеров, не имеющих детей.Постройте из найденных бездетных номеров вверх. –

+2

BTW: ваш предполагаемый выход '1,2,3,4 | 1,2,3,5,6' * не могут * существуют, поскольку каждый узел имеет только одно поле 'link' и, следовательно, только один преемник. («3» имеет как «4», так и «5» в качестве преемников. – wildplasser

+0

Глупый пользовательский интерфейс: я обрезал и отметил, что комментарий Эрвинса оскорбителен :-) – wildplasser

ответ

2

У вас уже есть решение у вас под рукой с cycle, просто добавьте предикат в конец.

Но корректировать состояние разрыва на одном уровне, в настоящее время вы добавив один узел слишком много:

WITH RECURSIVE search AS (
    SELECT id, link, data, ARRAY[g.id] AS path, (link = id) AS cycle 
    FROM graph g 
    WHERE NOT EXISTS (
     SELECT 1 
     FROM graph 
     WHERE link = g.id 
    ) 

    UNION ALL 
    SELECT g.id, g.link, g.data, s.path || g.id, g.link = ANY(s.path) 
    FROM search s 
    JOIN graph g ON g.id = s.link 
    WHERE NOT s.cycle 
    ) 
SELECT * 
FROM search 
WHERE cycle; 
-- WHERE cycle IS NOT FALSE; -- alternative if link can be NULL 
  • Также включает в себя условие запуска, как mentioned by @wildplasser.

  • Условие инициализации для cycle: (link = id), чтобы уловить циклы быстрого доступа. Не обязательно, если у вас есть ограничение CHECK, чтобы запретить это в вашей таблице.

  • Точная реализация зависит от недостающих деталей.

  • Это предполагает, что все графики завершаются с циклом или link IS NULL и существует ограничение FK от link к id в той же таблице. Точная реализация зависит от недостающих деталей. Если link на самом деле не является ссылкой (нет ссылочной целостности), вам необходимо адаптировать ...

+0

Это чрезвычайно полезно и очень красивое решение. – brechmos

2

Просто добавьте дополнительный пункт в окончательном запросе, как в:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
    SELECT g.id, g.link, g.data, 1, ARRAY[g.id], false 
    FROM graph g 
    -- BTW: you should add a START-CONDITION here, like: 
    -- WHERE g.id = 1 
    -- or even (to find ALL linked lists): 
    -- WHERE NOT EXISTS (SELECT 13 
      -- FROM graph nx 
      -- WHERE nx.link = g.id 
      --) 
    UNION ALL 
    SELECT g.id, g.link, g.data, sg.depth + 1, path || g.id, g.id = ANY(path) 
    FROM graph g, search_graph sg 
    WHERE g.id = sg.link AND NOT cycle 
) 
SELECT * FROM search_graph sg 
WHERE NOT EXISTS (-- <<-- extra condition 
    SELECT 42 FROM graph nx 
    WHERE nx.id = sg.link 
    ); 

Обратите внимание, что:

  • в not exists(...) -clause пытается присоединиться к точно та же самая запись, что и вторая нога рекурсивного союза.
  • Итак: они являются взаимоисключающими.
  • если он будет существует, он должен был быть прилагается к «списку» по рекурсивному запросу.
+0

Что делать, если результаты «1,2,3,4» и «1,2,4,5»? Не будет ли проигнорировано первое, хотя оно не является частью другого? –

+0

Нет, предложение is not exists пытается вставить * точно * ту же запись, что и вторая «нога» рекурсивного объединения. Итак: они взаимоисключающие. (если бы он существовал, он должен был быть добавлен в «список») – wildplasser

+0

Спасибо. Это полезно, я просто думал о соединении, которое было бы болезненным. И +1 для использования 42. – brechmos

Смежные вопросы