Почему PostgreSQL относится к моему запросу по -разному в функции?

StackOverflow https://stackoverflow.com/questions/9305133

  •  25-10-2019
  •  | 
  •  

Вопрос

У меня очень простой запрос, который не намного сложнее, чем:

select *
from table_name
where id = 1234

... для бега требуется менее 50 миллисекунд.

Взял этот запрос и поместил его в функцию:

CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN
    RETURN QUERY SELECT *
         FROM table_name
         where id = id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;

Эта функция при выполнении select * from pie(123); занимает 22 секунды.

Если я жесткий код целый ряд вместо ID_PARAM, функция выполняется менее чем в 50 миллисекундах.

Почему тот факт, что я использую параметр в операторе WHERE, заставляет мою функцию работать медленно?


Редактировать, чтобы добавить конкретный пример:

CREATE TYPE test_type AS (gid integer, geocode character varying(9))

CREATE OR REPLACE FUNCTION geocode_route_by_geocode(geocode_param character)
  RETURNS SETOF test_type AS
$BODY$
BEGIN
RETURN QUERY EXECUTE
    'SELECT     gs.geo_shape_id AS gid,     
        gs.geocode
    FROM geo_shapes gs
    WHERE geocode = $1
    AND geo_type = 1 
    GROUP BY geography, gid, geocode' USING geocode_param;
END;

$BODY$
  LANGUAGE plpgsql STABLE;
ALTER FUNCTION geocode_carrier_route_by_geocode(character)
  OWNER TO root;

--Runs in 20 seconds
select * from geocode_route_by_geocode('999xyz');

--Runs in 10 milliseconds
SELECT  gs.geo_shape_id AS gid,     
        gs.geocode
    FROM geo_shapes gs
    WHERE geocode = '9999xyz'
    AND geo_type = 1 
    GROUP BY geography, gid, geocode
Это было полезно?

Решение

Обновление в PostgreSQL 9.2

Было значительное улучшение, я цитирую выпуск заметок здесь:

Разрешить планировщику генерировать пользовательские планы для определенных значений параметров, даже при использовании подготовленных операторов (Tom Lane)

В прошлом у подготовленного утверждения всегда был один «общий» план, который использовался для всех значений параметров, который часто был намного уступал планам, используемым для не подготовленных операторов, содержащих явные постоянные значения. Теперь планировщик пытается создать пользовательские планы для определенных значений параметров. Общий план будет использоваться только после того, как пользовательские планы неоднократно доказали, что не приносят пользы. Это изменение должно устранить штрафы на производительность, ранее наблюдавшуюся с использованием подготовленных заявлений (включая не динамические операторы в PL/PGSQL).


Оригинальный ответ для PostgreSQL 9.1 или старше

Функции PLPGSQL оказывают аналогичный эффект, что и PREPARE Заявление: Запросы проанализированы, а план запроса кэширован.

Преимущество заключается в том, что некоторые накладные расходы сохраняются для каждого вызова.
Недостатком является то, что план запроса не оптимизирован для конкретных значений параметров, с которыми он вызывается.

Для запросов на таблицах с даже распределением данных это, как правило, не будет проблемой, и функции PL/PGSQL будут работать несколько быстрее, чем необработанные запросы SQL или функции SQL. Но если ваш запрос может использовать определенные индексы в зависимости от фактических значений в WHERE Пункт или, в более общем плане, выбрал лучший план запроса для конкретных значений, вы можете получить неоптимальный план запроса. Попробуйте функцию SQL или используйте динамический SQL с EXECUTE Чтобы заставить запрос, запланированный для каждого звонка. Могло бы выглядеть так:

CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN        
    RETURN QUERY EXECUTE
        'SELECT *
         FROM   table_name
         where  id = $1'
    USING id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;

Изменить после комментария:

Если этот вариант не изменяет время выполнения, должны быть и другие факторы, которые вы, возможно, пропустили или не упомянули. Разная база данных? Разные значения параметров? Вам придется опубликовать больше деталей.

Я добавляю цитату из руководства Чтобы подтвердить мои вышеупомянутые заявления:

Выполнение с простой строкой постоянной команды и некоторыми, использующими параметры, как в первом примере выше, функционально эквивалентно просто написанию команды непосредственно в PL/PGSQL и позволяет замену переменных PL/PGSQL автоматически. Важное отличие состоит в том, что Execute будет повторно планировать команду при каждом выполнении, генерируя план, который специфичен для текущих значений параметров; тогда как PL/PGSQL обычно создает общий план и кэширует его для повторного использования. В ситуациях, когда лучший план сильно зависит от значений параметров, выполнение может быть значительно быстрее; В то время как, когда план не чувствителен к значениям параметров, повторное планирование будет отходом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top