Как повысить производительность запросов, использующих табличные функции?

У меня есть запрос вида:

SELECT a.id, b.colb, c.colc, d.cold, ...
FROM a JOIN b on a.id=b.id
       JOIN c on a.id=c.id
       JOIN d on a.id=d.id
       JOIN e on a.id=e.id
       ...

Каждая таблица здесь на самом деле является функцией, возвращающей табличное значение. Время выполнения этого запроса составляет более минуты, но когда я вручную создаю временные таблицы (CREATE TEMPORARY TABLE ax as SELECT * from a) и запускаю запрос к ним, это занимает миллисекунды (и создание таблиц тоже занимает миллисекунды).

Что может быть причиной такой большой разницы в производительности (как минимум на два порядка)?


person jl6    schedule 06.04.2013    source источник
comment
Было бы полезно, если бы вы могли показать свою версию PostgreSQL и вывод explain analyze по вашему запросу.   -  person Craig Ringer    schedule 07.04.2013


Ответы (2)


Оптимизатор PostgreSQL может выбрать выполнение соединений как циклическое соединение. То есть он будет оценивать вызов функции для каждой левой строки.

Ваше решение с временными таблицами — отличный способ избежать этого. Даже если оптимизатор снова предпочтет loop join, результатом будет много операций поиска во временной таблице. Поиски на порядок дешевле, чем повторное вычисление функции.

person Andomar    schedule 06.04.2013
comment
Так ли это, даже если функция помечена как СТАБИЛЬНАЯ (т. е. не требует повторной оценки)? - person jl6; 07.04.2013
comment
STABLE просто говорит, что функция должна быть оценена только один раз для одного и того же набора значений. Возможно, все же потребуется оценить его один раз для каждой строки. Поскольку вы можете предварительно получить результат функции, это означает, что ваша функция менее изменчива, чем стабильна, и не зависит ни от какой строки. STABLE не сообщает оптимизатору всего этого. - person Andomar; 07.04.2013

Попробуйте CTE:

with
    a as (select * from a),
    b as (select * from b),
    c as (select * from c),
    d as (select * from d),
    e as (select * from e)
select a.id, b.colb, c.colc, d.cold, ...
from a
    join b on a.id=b.id
    join c on a.id=c.id
    join d on a.id=d.id
    join e on a.id=e.id
person Clodoaldo Neto    schedule 06.04.2013