Postgres просмотр условий pushdown не работает при соединении - PullRequest
0 голосов
/ 28 мая 2018

Я наблюдал странное поведение postgres и застрял с правильной оптимизацией запросов.

Структура и тестовые данные:

CREATE table t_base(
    id serial PRIMARY KEY,
    value text
);    

SELECT *
FROM t_base;

CREATE TABLE t1 (
    id serial PRIMARY KEY,
    base_id int REFERENCES t_base(id),
    value text
);

CREATE TABLE t2 (
    id serial PRIMARY KEY,
    base_id int REFERENCES t_base(id),
    value text
);

CREATE VIEW v_all AS
    SELECT
        id, base_id, value, 't1' as tname
    FROM t1
    UNION ALL
    SELECT
        id, base_id, value, 't2' as tname
    FROM t2;

CREATE TABLE t_data (
    tname text,
    t_id int
);

INSERT INTO t_base (value)
SELECT 'val' || i FROM generate_series(1, 100000) s(i);

INSERT INTO t1 (base_id, value)
SELECT i, 't1_val' || i FROM generate_series(1, 50000) s(i);

INSERT INTO t2 (base_id, value)
SELECT i, 't2_val' || i FROM generate_series(50001, 100000) s(i);

INSERT INTO t_data VALUES ('t1', 1), ('t1', 4);

INSERT INTO t_data
SELECT 't1', (random()*100)::int
FROM generate_series(1, 3000) s(i);

VACUUM ANALYZE VERBOSE t_base;
VACUUM ANALYZE VERBOSE t1;
VACUUM ANALYZE VERBOSE t2;
VACUUM ANALYZE VERBOSE t_data;

Представление v_all в этом случае упрощается, на самом деле яв нем 9 таблиц, и в большинстве из них много строк.

Теперь я пытаюсь сделать запрос:

EXPLAIN ANALYZE
SELECT *
FROM v_all
WHERE tname = 't1' and id = 2;

QUERY PLAN                                                                                                        
----------------------------------- 
 Append  (cost=0.29..8.31 rows=1 width=51) (actual time=0.056..0.058 rows=1 loops=1)                               
   ->  Index Scan using t1_pkey on t1  (cost=0.29..8.31 rows=1 width=51) (actual time=0.055..0.056 rows=1 loops=1) 
         Index Cond: (id = 2)                                                                                      
 Planning time: 0.264 ms                                                                                           
 Execution time: 0.103 ms

Отлично!Именно то, что я хочу:

  1. Таблица только для сканирования t1
  2. Используемый индекс

Теперь я хочу сделать то же самое с помощью соединения:

EXPLAIN ANALYZE
SELECT *
FROM t_data d
    JOIN v_all v ON (v.tname = d.tname AND v.id = d.t_id);

 QUERY PLAN                                                                                                                 
 ------------------------------------------------------------------
 Nested Loop  (cost=0.29..3427.50 rows=3840 width=58) (actual time=0.058..15.649 rows=2986 loops=1)                         
   ->  Seq Scan on t_data d  (cost=0.00..44.00 rows=3000 width=7) (actual time=0.023..0.727 rows=3000 loops=1)              
   ->  Append  (cost=0.29..1.11 rows=2 width=51) (actual time=0.003..0.004 rows=1 loops=3000)                               
         ->  Index Scan using t1_pkey on t1  (cost=0.29..0.92 rows=1 width=51) (actual time=0.002..0.003 rows=1 loops=3000) 
               Index Cond: (id = d.t_id)                                                                                    
               Filter: (d.tname = 't1'::text)                                                                               
         ->  Index Scan using t2_pkey on t2  (cost=0.15..0.19 rows=1 width=72) (actual time=0.001..0.001 rows=0 loops=3000) 
               Index Cond: (id = d.t_id)                                                                                    
               Filter: (d.tname = 't2'::text)                                                                               
 Planning time: 0.626 ms                                                                                                    
 Execution time: 16.095 ms 

И результат не ожидаю, что таблицы t1 и t2 будут сканироваться для поиска по идентификатору, вместо того, чтобы просто пропустить эту "ветвь" по константе, которая представлена ​​как Filter: (d.tname = 't1'::text)

Iпопробовал оба postgres 10.3 и 9.4:

select version();
PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit

Есть ли методика, чтобы научить postgres не смотреть на таблицу в представлении с предложением UNION ALL, просто проверив сначала условие константы на JOINs?

SqlFiddle

1 Ответ

0 голосов
/ 29 мая 2018

Пожалуйста, помните, что PostgreSQL «фиксирует» план выполнения после 5 выполнений.

Это означает, что если он ошибается или не завершается после пятого выполнения, он не будет исправлять его впоследствии - пока вы не перезапуститеинициализируйте его.

  • В первом примере PostgreSQL избегает второй таблицы, поскольку вы использовали постоянные значения.Нулевой шанс, что понадобится другой стол.PostgreSQL может немедленно зафиксировать план в таком случае, поскольку сканирование другой таблицы не требуется - вообще.

  • Однако во втором случае PostgreSQL не может исправитьплан и исключить вторую таблицу навсегда .Может быть разумно удалить его сейчас, но как насчет завтра или недели спустя?PostgreSQL не может рисковать, фиксируя план выполнения, подходящий для текущих значений ... который может быть неправильным в будущем.Вот почему он «держит свои возможности открытыми», если говорить простым языком.

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

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