Смотрите подробные объяснения в статье в моем блоге:
Если ваших пользователей можно найти более одного раза в столбце needs
таблицы, это сложная задача NP
.
Если нет, введите следующий запрос:
SELECT COUNT(*)
FROM v_needs
CONNECT BY NOCYCLE
need = PRIOR have
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE
разрешает циклы в иерархических запросах (NOCYCLE
просто останавливает ветвление, когда находит циклы, нет NOCYCLE
возвращает ошибку).
CONNECT_BY_ISCYCLE
возвращает истину всякий раз, когда находит цикл (для записи возвращается 1
, которая выдаст корень текущей ветви на следующей итерации).
Обратите внимание, что этот запрос выдаст вам всех пользователей в циклах без их группировки.
Чтобы сгруппировать пользователей, выполните этот запрос:
SELECT n.*, CONNECT_BY_ROOT(have), level
FROM v_needs n
START WITH
have IN
(
SELECT MIN(have)
FROM (
SELECT have, CONNECT_BY_ROOT(have) AS root
FROM t_needs
START WITH
have IN
(
SELECT have
FROM t_needs n
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE
needs = PRIOR have
)
CONNECT BY NOCYCLE
have = PRIOR needs
)
GROUP BY
root
)
CONNECT BY NOCYCLE
have = PRIOR needs
Обновление:
Из вашего поста я вижу, что у вас есть ограничение на максимальную длину цикла.
Это значительно облегчает решение этой проблемы.
Просто добавьте условие ограничения цикла в каждый из запросов:
SELECT n.*, CONNECT_BY_ROOT(have), level
FROM v_needs n
START WITH
have IN
(
SELECT MIN(have)
FROM (
SELECT have, CONNECT_BY_ROOT(have) AS root
FROM t_needs
START WITH
have IN
(
SELECT have
FROM t_needs n
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE
needs = PRIOR have
AND level <= 5
)
CONNECT BY NOCYCLE
have = PRIOR needs
AND level <= 5
)
GROUP BY
root
)
CONNECT BY NOCYCLE
have = PRIOR needs
AND level <= 5
Это остановит обход иерархического дерева на уровне 5th
.
Если у вас есть 1,000,000
пользователей в вашей базе данных и 5
совпадений на пользователя в среднем, запрос должен будет проверить 1,000,000 * 5^5 = 3,125,000,000
строк, что является наблюдаемым числом строк.
Запрос будет значительно улучшен, если вы MATERIALIZE
представите свое представление и создадите индексы 'need' и 'have'.
В этом случае выполнение запроса займет считанные минуты.