Как оптимизировать запрос 2 таблиц, где данные могут быть различены только на основе обеих таблиц? - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть следующие 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)

1 Ответ

0 голосов
/ 28 сентября 2018

1) Вы можете использовать материализованное представление с соответствующим индексом.Может обновляться в "фоновом режиме".В противном случае он похож на ваш составной индекс в строке.

2) Вы можете обратить поиск к заголовку к строке, если вы создадите индекс для (line.header_id, line.type) и принудительно выполните подзапрос следующим образом:

select header_id 
from header h 
where type='A' and 
    exists(select * from line l where l.header_id=h.header_id and l.type='B')

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

Возможно, было бы неплохо включить тип в индекс некоторых заголовков, чтобы 2 индекса были всем необходимым для поиска.

Тем не менее он будет читать ~ 50К строк в индексе заголовков и искать каждый из них.из них во 2-м указателе.В общем, это не эффективно, но если индексы полностью помещаются в память, это может быть не так уж плохо.

...