То, что вы пытаетесь выполнить, sh определенно возможно с помощью рекурсивных CTE. Я расскажу о первых двух сценариях ios, которые у вас есть, так как два других являются просто продолжением первых двух.
Во всех SQL примерах я собираюсь использовать идентификатор 1 для иллюстрации значения что вы заменяете на уровне модели. Поскольку вы написали этот запрос, я собираюсь немного познакомиться с рекурсивными CTE и попытаться найти решение.
get_all_children
Давайте сначала рассмотрим метод get_all_children
. Этот метод включает в себя переход по дереву, уровень за уровнем и покрытие узлов, с которыми мы сталкиваемся.
Так как pod_pods содержит всю информацию, касающуюся иерархии, и в получении дочерних элементов нет никакой вовлеченной области, мы можем просто использовать pod_pods для дети.
-- Snippet #1
WITH RECURSIVE pod_tree AS (
SELECT pod_id -- Get the pod_id of the children of the base case node
FROM pod_pods
WHERE parent_id = 1 -- Base case
UNION ALL -- Recurse on this and do a union with the previous step
SELECT p.pod_id
FROM pod_pods p
INNER JOIN pod_tree ptree
ON ptree.pod_id = p.parent_id -- Get the children nodes for nodes found at the previous recursion step.
)
SELECT * FROM pods
WHERE id IN (SELECT DISTINCT(pod_id) FROM pod_tree);
Ваш код Ruby не покрывает вероятность бесконечного l oop, происходящего из-за цикла, но если есть вероятность, что это может произойти, то, как вы будете решать это отслеживая идентификаторы, которые вы уже видели.
-- Snippet #2
WITH RECURSIVE pod_tree(pod_id, rtree) AS ( -- Extra rtree parameter to keep track of visited nodes
SELECT pod_id, ARRAY[pod_id] -- Make the base case array with pod_id
FROM pod_pods
WHERE parent_id = 1 -- Base case
UNION ALL
SELECT p.pod_id, rtree || p.pod_id -- Add the current pod_id to array
FROM pod_pods p
INNER JOIN pod_tree ptree
ON ptree.pod_id = p.parent_id
WHERE NOT (p.pod_id = ANY(rtree)) -- Exclude nodes which have already been seen
)
SELECT * FROM pods
WHERE id IN (SELECT DISTINCT(pod_id) FROM pod_tree);
Если у вас могут быть бесхозные отношения в pod_pods и вы хотите их игнорировать, тогда необходимо объединение между модулями.
-- Snippet #3
WITH RECURSIVE pod_tree(id, rtree) AS (
SELECT p1.id, ARRAY[p1.id]
FROM pods p1 INNER JOIN pod_pods p2 ON p1.id = p2.pod_id
WHERE parent_id = 1
UNION ALL
SELECT p1.id, rtree || p1.id
FROM pods p1
INNER JOIN pod_pods p2 ON p1.id = p2.pod_id
INNER JOIN pod_tree ptree ON p2.parent_id = ptree.id
WHERE NOT (p1.id = ANY(ptree.rtree))
)
SELECT * FROM pods WHERE id IN (SELECT DISTINCT(id) FROM pod_tree);
Если у вас нет бесхозных ссылок, я бы посоветовал вам go с фрагментом № 1 или № 2, так как они будут быстрее, чем № 3, так как это требует дополнительных объединений.
get_all_parents
Сначала для простоты добавим поля области действия, которые добавляются из-за активности позже. Сначала мы просто спустимся по дереву таблицы pod_pods, чтобы получить все параметры. вводит идентификаторы, а затем мы применяем область действия.
-- Snippet #4
WITH RECURSIVE pod_tree AS (
SELECT parent_id -- Get the parent_id of the parents of the base case node
FROM pod_pods
WHERE pod_id = 1 -- Base case
UNION ALL -- Recurse on this and do a union with the previous step
SELECT p.parent_id
FROM pod_pods p
INNER JOIN pod_tree ptree
ON ptree.parent_id = p.pod_id -- Get the parent nodes for nodes found at the previous recursion step.
)
SELECT * FROM pods
WHERE
id IN (SELECT DISTINCT(parent_id) FROM pod_tree)
AND pod_state = 'active'
AND pod_type IN ('standard', 'readonly')
;
Однако активный фильтр применяется только после того, как все узлы выбраны. Это не может быть идеальным, так как может пройти больше дерева, чем требуется, и даже может вернуть родителей узлов, которые не активны. Чтобы сделать так, как это делает метод в коде Ruby, нам нужно объединить его с модулями. Я добавляю здесь шаг, позволяющий избежать бесконечной рекурсии, и теперь у вас есть представление об этом.
-- Snippet #5
WITH RECURSIVE pod_tree(id, rtree) AS (
SELECT p1.id, ARRAY[p1.id]
FROM pods p1
INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
WHERE pod_id = 1
AND p1.pod_state = 'active'
AND p1.pod_type IN ('standard', 'readonly')
UNION ALL
SELECT p1.id, rtree || p1.id
FROM pods p1
INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
INNER JOIN pod_tree ptree ON p2.pod_id = ptree.id
WHERE p1.pod_state = 'active'
AND p1.pod_type IN ('standard', 'readonly')
AND NOT (p1.id = ANY(ptree.rtree))
)
SELECT * FROM pods WHERE id IN (SELECT DISTINCT(id) FROM pod_tree);
В Rails, основанном на вашем методе-заглушке, код для фрагмента # 5 будет выглядеть как
def get_all_parents
sql =
<<-SQL
WITH RECURSIVE pod_tree(id, rtree) AS (
SELECT p1.id, ARRAY[p1.id]
FROM pods p1
INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
WHERE pod_id = #{self.id}
AND p1.pod_state = 'active'
AND p1.pod_type IN ('standard', 'readonly')
UNION ALL
SELECT p1.id, rtree || p1.id
FROM pods p1
INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
INNER JOIN pod_tree ptree ON p2.pod_id = ptree.id
WHERE p1.pod_state = 'active'
AND p1.pod_type IN ('standard', 'readonly')
AND NOT (p1.id = ANY(ptree.rtree))
)
SELECT * FROM pods WHERE id IN (SELECT DISTINCT(id) FROM pod_tree);
SQL
# IMP!
# sql = sql_sanitize(sql)
# Add some sanitize step here
sql.chomp
Pod.find_by_sql(sql)
end
Это должно охватывать ваши первые два варианта использования. Как упоминалось ранее, два других являются расширением этих двух, так что вы можете просто использовать их, чтобы расширить их.
Примечание:
- Если у вас нет циклов, Вы можете избежать бесконечных столбцов рекурсии, так как это дополнительная бухгалтерия.
- Если у вас нет бесхозных ссылок, предпочтите итерацию только для
pod_pods
для детей, поскольку это позволяет избежать ненужных объединений rtree
в указанных выше sql запросах содержится иерархия. Вы можете передать его обратно, если вам нужна эта информация. Я пропустил его, так как вы все равно в итоге сгладили результат. - Я выбирал уникальные узлы. Ваш код Rails в настоящее время будет извлекать несколько вхождений узла, если он посещается несколько раз. Если вы хотите это, а также порядок дерева, вы можете иметь следующее поведение:
-- Example for getting all parents
WITH RECURSIVE pod_tree(id, slug, pod_type, parent_id, rtree) AS (
SELECT p1.id, p1.slug, p1.pod_type, p2.parent_id, ARRAY[p1.id] -- Select the fields you need
FROM pods p1 INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
WHERE pod_id = 1
AND p1.pod_state = 'active' AND p1.pod_type IN ('standard', 'readonly')
UNION ALL
SELECT p1.id, p1.slug, p1.pod_type, p2.parent_id, rtree || p1.id
FROM pods p1 INNER JOIN pod_pods p2 ON p1.id = p2.parent_id
INNER JOIN pod_tree ptree ON p2.pod_id = ptree.id
WHERE p1.pod_state = 'active' AND p1.pod_type IN ('standard', 'readonly')
AND NOT (p1.id = ANY(ptree.rtree))
)
SELECT * FROM pod_tree;