Могу ли я как-то предоставить дополнительный контекст, чтобы позволить postgres эффективно сортировать / ограничивать представления без вычисления их строк? - PullRequest
0 голосов
/ 12 июня 2019

Учитывая этот несколько надуманный запрос

select id, pg_sleep(0.001)::text from administrative_areas;

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

> explain analyze select id, pg_sleep(0.001)::text from administrative_areas order by id desc limit 1;

Limit  (cost=0.28..0.39 rows=1 width=36) (actual time=4.227..4.228 rows=1 loops=1)
  ->  Index Only Scan Backward using administrative_areas_pkey on administrative_areas  (cost=0.28..69.50 rows=604 width=36) (actual time=4.227..4.227 rows=1 loops=1)
        Heap Fetches: 1
Planning time: 0.066 ms
Execution time: 4.243 ms

Если я брошу тот же запрос в представлении

CREATE OR REPLACE VIEW sleepy AS
    select id, pg_sleep(0.001)::text from administrative_areas;

При запросе с ордером и ограничением режим сна выполняется один раз для каждого элемента в лежащей в основе таблице administrative_areas.

> explain analyze select * from sleepy order by id desc limit 1;

Limit  (cost=30.63..30.63 rows=1 width=36) (actual time=3794.827..3794.829 rows=1 loops=1)
  ->  Sort  (cost=30.63..32.14 rows=604 width=36) (actual time=3794.825..3794.825 rows=1 loops=1)
        Sort Key: administrative_areas.id DESC
        Sort Method: top-N heapsort  Memory: 25kB
        ->  Seq Scan on administrative_areas  (cost=0.00..21.57 rows=604 width=36) (actual time=6.432..3792.566 rows=604 loops=1)
Planning time: 0.072 ms
Execution time: 3794.851 ms

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

1 Ответ

0 голосов
/ 12 июня 2019

Я полагаю, это потому, что pg_sleep является изменчивой функцией. Когда вы запрашиваете представление, вы действуете так:

select id from (select id, pg_sleep(0.001)::text from administrative_areas) order by id desc limit 1;

Postgres видит эту изменчивую функцию в подзапросе и запускает ее для каждой строки. Давайте проверим это.

create table test as select id from generate_series(1, 1000) g(id);
create index on test(id);
analyze test;
create view sleepy as select id, pg_sleep(0.001)::text from test;

explain analyze select * from sleepy order by id desc limit 1;
                                                     QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
 Limit  (cost=37.50..37.50 rows=1 width=36) (actual time=1640.368..1640.439 rows=1 loops=1)
   ->  Sort  (cost=37.50..40.00 rows=1000 width=36) (actual time=1640.336..1640.358 rows=1 loops=1)
         Sort Key: test.id DESC
         Sort Method: top-N heapsort  Memory: 25kB
         ->  Seq Scan on test  (cost=0.00..22.50 rows=1000 width=36) (actual time=1.511..1623.058 rows=1000 loops=1)
 Planning Time: 0.175 ms
 Execution Time: 1640.617 ms
(7 rows)

Это запустило pg_sleep для каждой строки в тесте, как и ожидалось.

Теперь попробуйте стабильную функцию:

create function not_so_sleepy() 
  returns void AS 
  $$ 
    select pg_sleep(0.001) 
  $$ language sql 
stable;  -- NOTE: this is just to trick postgres


create view not_as_sleepy as 
  select id, 
  not_so_sleepy()::text 
FROM test;

explain analyze select * 
  from not_as_sleepy 
  order by id desc limit 1;
                                                                QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..0.32 rows=1 width=36) (actual time=0.049..0.198 rows=1 loops=1)
   ->  Index Only Scan Backward using test_id_idx on test  (cost=0.28..43.27 rows=1000 width=36) (actual time=0.024..0.048 rows=1 loops=1)
         Heap Fetches: 1
 Planning Time: 1.786 ms
 Execution Time: 0.308 ms
(5 rows)

Во втором случае мы сказали postgres, что эта функция не будет иметь побочных эффектов, поэтому она могла бы ее игнорировать. Таким образом, функция должна быть помечена как стабильная или неизменная (и, конечно, она должна быть фактически стабильной / неизменной), чтобы postgres не беспокоил ее выполнение.

...