У меня есть следующие 2 таблицы и распределение данных:
drop table if exists line;
drop table if exists header;
create table header (header_id serial primary key, type character);
create table line (line_id serial primary key, header_id serial not null, type character, constraint line_header foreign key (header_id) references header (header_id)) ;
create index inv_type_idx on header (type);
create index line_type_idx on line (type);
insert into header (type) select case when floor(random()*2+1) = 1 then 'A' else 'B' end from generate_series(1,100000);
insert into line (header_id, type) select header_id, case when floor(random()*10000+1) = 1 then (case when type ='A' then 'B' else 'A' end) else type end from header, generate_series(1,5);
header
таблица имеет 100K строк: 50% type
A и 50% B - * Таблица 1009 * имеет 500 тыс. Строк:
- каждая
header
имеет 5 line
s - , в целом 50% строк
type
A и 50% B type
для line
совпадает с его header
в 99,99% случаев, только в 0,01% они отличаются
Данныераспределение:
# select h.type header_type, l.type line_type, count(*) from line l inner join header h on l.header_id = h.header_id group by 1,2 order by 1,2;
header_type | line_type | count
-------------+-----------+--------
A | A | 250865
A | B | 25
B | A | 29
B | B | 249081
(4 rows)
Мне нужно получить все line
s с type
B, чье header
равно A. Даже общая сумма очень ограничена (25 из 500000 строк) плана Iполучить (PostgreSQL 10) является следующим, который выполняет последовательное сканирование в обеих таблицах:
explain
select * from line l
inner join header h on l.header_id = h.header_id
where h.type ='A' and l.type='B';
QUERY PLAN
---------------------------------------------------------------------------
Hash Join (cost=2323.29..14632.89 rows=125545 width=19)
Hash Cond: (l.header_id = h.header_id)
-> Seq Scan on line l (cost=0.00..11656.00 rows=248983 width=13)
Filter: (type = 'B'::bpchar)
-> Hash (cost=1693.00..1693.00 rows=50423 width=6)
-> Seq Scan on header h (cost=0.00..1693.00 rows=50423 width=6)
Filter: (type = 'A'::bpchar)
(7 rows)
Есть ли способ оптимизировать такого рода запросы, где дискриминация данных очень высокано только при объединении информации из более чем одной таблицы?
Конечно, в качестве обходного пути я мог бы денормализовать хранение информации в line
s информации из header
wкоторый сделал бы этот запрос намного более производительным.Но если возможно, я бы предпочел не делать этого, потому что мне нужно было бы сохранить эту дублированную информацию.
alter table line add column compound_type char(2);
create index compound_idx on line (compound_type);
update line l
set compound_type = h.type || l.type
from header h
where h.header_id = l.header_id;
# explain select * from line where compound_type = 'BA';
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using compound_idx on line (cost=0.42..155.58 rows=50 width=13)
Index Cond: (compound_type = 'BA'::bpchar)
(2 rows)