Я наблюдал странное поведение 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
Отлично!Именно то, что я хочу:
- Таблица только для сканирования
t1
- Используемый индекс
Теперь я хочу сделать то же самое с помощью соединения:
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