PostgreSQL с рекурсивной производительностью - PullRequest
14 голосов
/ 02 мая 2011

У меня простой вопрос. Каким-то образом я не смог найти окончательный ответ.

Насколько оптимизирован синтаксис WITH RECURSIVE в PostgreSQL? Под этим я подразумеваю: является ли это просто синтаксическим сахаром для серии нерекурсивных запросов, ИЛИ это скорее единое утверждение, которое, несмотря на его сложную семантику, было оптимизировано в целом. Последующий вопрос - насколько можно оптимизировать этот синтаксис? Конечно, некоторые конкретные данные по этому вопросу приветствуются.

Ответы [ 2 ]

18 голосов
/ 08 мая 2011

Я нашел его оптимизированным до определенного момента.

Различные подзапросы используются повторно, как и ожидалось, и оптимизируются индивидуально, а Postgres оптимизирует последний так же, как и любой другой запрос.

Моя основная неприятность связана с тем, что он не будет вводить ограничения в CTE, когда это возможно.

Например:

with recursive
parents as (
select node.id,
       node.parent_id
from nodes as node
union all
select node.id,
       parent.parent_id
from parents as node
join nodes as parent on parent.id = node.parent_id
)
select parent_id
from parents
where id = 2;

В идеале Postgres будет идеально понимать, как указано выше., что (поскольку node.id возвращается как ), он может:

with recursive
parents as (
select node.id,
       node.parent_id
from nodes as node
where id = 2
union all
select node.id,
       parent.parent_id
from parents as node
join nodes as parent on parent.id = node.parent_id
)
select parent_id
from parents;

... и использовать сканирование индекса по первичному ключу.На практике это будет действительно именно тогда, когда CTE скажет, что нужно: рекурсивно извлекать все родительские элементы для всех строк, при необходимости помещать результирующий набор в неназванную временную таблицу, а затем проверять каждую строку из результирующего набора на id =2.

Другими словами, CTE не сохраняет след «исходного» набора таблиц / строк / столбцов, который он возвращает.До тех пор, пока это не будет должным образом оптимизировано, создание представления для рекурсивного запроса в лучшем случае безумие.

Тем временем хорошим обходным путем является создание функции sql:

create function parents(id int) as returns table (id int) $$
    with recursive
    parents as (
    select node.id,
           node.parent_id
    from nodes as node
    where id = $1
    union all
    select node.id,
           parent.parent_id
    from parents as node
    join nodes as parent on parent.id = node.parent_id
    )
    select parent_id
    from parents;
$$ language sql stable strict rows 5 cost 1;

Другая проблема заключается ввы не можете использовать FOR UPDATE с рекурсивными CTE (фактически, по той же причине).

4 голосов
/ 02 мая 2011

Мой опыт показывает, что он действительно очень хорошо оптимизирован.

Проверьте план выполнения для вашего запроса, сгенерированного EXPLAIN ANALYZE, и вы увидите, насколько это "дорого" на самом деле(а затем сравните это, например, с самореписанной рекурсивной функцией)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...