Возможно ли сокращение ветви для рекурсивного запроса cte - PullRequest
0 голосов
/ 28 января 2020

Это основано на вопросе Получить список списков в одном SQL утверждении - Я нашел решение, но у меня есть сомнения в его эффективности.

Переформулируем проблема:

  • у нас есть 2 таблицы: Person и Parent
  • Person содержит основы c данные о каждом человеке
  • Parent это объединяющая таблица, связывающая человека с его родителями
  • у каждого человека может быть несколько родителей
  • мы хотим получать данные о каждом человеке со списком всех их предков - каждого предка в своем собственном ряду
  • если нет предков, у нас есть только одна строка для этого человека с нулевым parentId

Вот формат данных:

Таблица персон

Id <PK>
Name

Родительская таблица

Id<PK>
ParentPersonId <FK into Person >

У человека есть строки со значениями PK

1, 'Jim'
2, 'John'
3, 'Anna'
4, 'Peter'

У родителя есть строки со значениями

1, 2
1, 3
2, 3
3, 4

Таким образом, у человека 1 есть предки 2 , 3, 4

Я хочу вывод в следующем виде

id  name    parentPersonId
--------------------------
1   Jim     2
1   Jim     3
1   Jim     4
2   John    3
2   John    4
3   Anna    4
4   Peter   (null)

В моем решении используется рекурсивный CTE запрос, но я боюсь, что он производит слишком много строк, так как каждое поддерево может быть введено несколько раз. Мне нужно было отфильтровать дубликаты с различными данными, и план выполнения показывает, что даже с этими простыми данными сортировка по различным занимает 50% времени. Вот мой запрос:

WITH cte_org AS 
(
    SELECT per.id, per.name, par.parentPersonId
    FROM Person per 
    LEFT JOIN Parent par ON per.id = par.id
    UNION ALL
    SELECT o.id, o.name, rec.parentPersonId
    FROM Parent rec
    INNER JOIN cte_org o ON o.parentPersonId = rec.id
    WHERE rec.parentPersonId IS NOT NULL
)
SELECT DISTINCT *
FROM cte_org
ORDER BY id, parentPersonId;

http://sqlfiddle.com/#! 18 / d7d62 / 4

Мои вопросы:

  • могу ли я каким-то образом обрезать уже посещенные ветви, чтобы рекурсивный CTE не создавал дублирующихся строк, и окончательное различение не требуется
  • является ли рекурсивный CTE правильным подходом к этой проблеме?

1 Ответ

1 голос
/ 28 января 2020

На PostgreSQL вы можете добиться этого, заменив UNION ALL на UNION. Таким образом, запрос выглядит так:

WITH RECURSIVE cte_org AS (
    select per.id, per.name, par.parentPersonId
    from Person per left join Parent par 
    on per.id = par.id
    UNION 
    SELECT 
        o.id, 
        o.name,
        rec.parentPersonId
    FROM 
        Parent rec
        INNER JOIN cte_org o 
            ON o.parentPersonId = rec.id
        where rec.parentPersonId is not null
)
SELECT *
FROM cte_org
ORDER BY id, parentPersonId;

http://sqlfiddle.com/#! 17 / 225cf4 / 4

...