Мое решение
Следующий запрос - это решение, которое я использовал (:node_a
и :node_b
- это заполнители для рассматриваемого узла и его возможного предка):
WITH RECURSIVE ancestors AS(
SELECT parent, id=:node_b AS match
FROM NODES WHERE id=:node_a
UNION ALL
SELECT n.parent, n.id=:id AS match
FROM Nodes n JOIN ancestors
ON ancestors.parent=n.id AND NOT ancestors.match
)
SELECT MAX(match) AS is_ancestor FROM ancestors;
Объяснение
Общее табличное выражение используется для генерации запроса, содержащего родителя каждого из предков node_a, и соответствует ли этот предок node_b
Он начинается с самого node_a и проверяет, совпадает ли это с node_b, возвращая строку, состоящую из двух столбцов parent
и match
- прямого родителя для node_a и логического значения, представляющего, соответствует ли node_a для node_b.
Затем он поднимается вверх через предков node_a путем рекурсивного присоединения к родительскому столбцу, пока один из предков не совпадет с node_b или родительский столбец не станет равным NULL.
Если node_b является предком node_a, тогда одна строка этой таблицы будет содержать значение true для столбца соответствия. Если это не так, тогда столбец соответствия будет содержать значение false для всех строк. Значения true и false представлены как 1 и 0, соответственно, MAX столбца соответствия будет 1 в первом случае и 0 во втором.
Поэтому весь запрос возвращает 1, если node_b является предком node_a и 0 в противном случае.