Как оценивается параметр `rows` в` объяснять анализ` (postgresql) в случае сканирования функции? - PullRequest
0 голосов
/ 11 апреля 2020

Я проверял планы выполнения для частей сложного запроса и придумал следующее:

postgres=# explain analyze                                                                                                                                                
select * from generate_series(
            (CURRENT_DATE)::timestamp without time zone,
            (CURRENT_DATE + '14 days'::interval),
            '1 day'::interval)
;
                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series  (cost=0.01..10.01 rows=1000 width=8) (actual time=0.024..0.036 rows=15 loops=1)
 Planning Time: 0.031 ms
 Execution Time: 0.064 ms
(3 rows)


AFAIK, postgresql оценивает строки на основе размера релтулов для данной таблицы, это понятно.

Учитывая, что упомянутый generate_series фактически генерирует 14 строк, откуда берется rows=1000 в случае сканирования функции?

Ответы [ 4 ]

1 голос
/ 11 апреля 2020

В соответствии с документацией :

Для тех, кто заинтересован в дальнейших деталях, оценка размера таблицы (до любых предложений WHERE) выполняется в src / backend / оптимизатор / Util / plancat. c. Общий c logi c для выбора предложений находится в src / backend / optimizer / path / clausesel. c. Специфичные для оператора c функции селективности в основном находятся в src / backend / utils / adt / selfuncs. c.

это функция, которая вычисляет оценки для функций:

/*
  * function_selectivity
  *
  * Returns the selectivity of a specified boolean function clause.
  * This code executes registered procedures stored in the
  * pg_proc relation, by calling the function manager.
  *
  * See clause_selectivity() for the meaning of the additional parameters.
  */
 Selectivity
 function_selectivity(PlannerInfo *root,
                      Oid funcid,
                      List *args,
                      Oid inputcollid,
                      bool is_join,
                      int varRelid,
                      JoinType jointype,
                      SpecialJoinInfo *sjinfo)
 {

Похоже, эта C функция будет читать данные в системном каталоге pg_pro c, где мы имеем:

postgres=# select proname, prosupport, prorows 
           from pg_proc 
           where proname like '%generate%';
           proname            |          prosupport          | prorows 
------------------------------+------------------------------+---------
 generate_subscripts          | -                            |    1000
 generate_subscripts          | -                            |    1000
 generate_series              | generate_series_int4_support |    1000
 generate_series              | generate_series_int4_support |    1000
 generate_series_int4_support | -                            |       0
 generate_series              | generate_series_int8_support |    1000
 generate_series              | generate_series_int8_support |    1000
 generate_series_int8_support | -                            |       0
 generate_series              | -                            |    1000
 generate_series              | -                            |    1000
 generate_series              | -                            |    1000
 generate_series              | -                            |    1000
(12 rows)

Похоже, что столбец pg_pro c .prorows - это извлеченный оценка.

0 голосов
/ 11 апреля 2020

Какой смысл. Это очень явный пример преждевременной оптимизации. В этом случае результат возвращается раньше, чем мозг осознает, что вы «нажали» бег.

Реальная проблема заключается в том, что программисты слишком много времени уделяют заботам об эффективности в неправильных местах и ​​в неподходящее время; преждевременная оптимизация - это root всего зла (или, по крайней мере, большей его части) в программировании.

Дональд Кнут, Искусство компьютерного программирования , 1962.

Казалось бы, сегодня это, по крайней мере, такая же большая проблема, как и тогда.

0 голосов
/ 11 апреля 2020

В качестве очевидного обходного пути вы можете обмануть планировщика, заключив запрос в подзапрос с ПРЕДЕЛОМ:


select * FROM (
        select * from generate_series(
            (CURRENT_DATE)::timestamp without time zone,
            (CURRENT_DATE + '14 days'::interval),
            '1 day'::interval)
        LIMIT 15;
        ) xxx
    ;
0 голосов
/ 11 апреля 2020

Функции generate_series, которые принимают числовые аргументы, имеют «вспомогательную функцию», которая просматривает аргументы, а затем сообщает планировщику, сколько строк ожидать. Но те, которые имеют дело с метками времени, не имеют таких функций поддержки. Вместо этого он просто оценивает, что возвращает предварительные строки, которые по умолчанию равны 1000.

Вы можете изменить эту оценку, если хотите:

alter function generate_series(timestamp without time zone, timestamp without time zone, interval) 
    rows 14;

Но это изменение не переживет ни pg_upgrade, ни dump / reload.

Это спецификация версии c, так как функции поддержки были реализованы только в v12. До этого даже числовые формы всегда планировались на 1000 строк (или любые другие значения, заданные для этой функции).

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