Postgres несколько предикатов в нескольких столбцах - PullRequest
0 голосов
/ 04 октября 2019

Отредактировано:

Я подумал, что объясню, что я пытаюсь сделать, чтобы кто-то мог лучше понять, как написать запрос лучше, чем я спрашивал.

I 'у нас есть таблица, содержащая около 500 миллионов строк, а другая - около 50 миллионов строк.

Определения таблицы похожи на следующие

CREATE TABLE NGRAM_CONTENT
(
    id  BIGINT NOT NULL PRIMARY KEY,
    ref TEXT   NOT NULL,
    data      TEXT
);

CREATE INDEX idx_reference_ngram_content ON NGRAM_CONTENT (ref);
CREATE INDEX idx_id_ngram_content ON NGRAM_CONTENT (id);


CREATE TABLE NGRAMS
(
    id  BIGINT NOT NULL,
    ngram   TEXT   NOT NULL,
    ref TEXT   NOT NULL,
    name_length INT NOT NULL
);

CREATE INDEX combined_index ON NGRAMS (name_length, ngram, ref, id);
CREATE INDEX namelength_idx ON NGRAMS (name_length);
CREATE INDEX id_idx ON NGRAMS (id);
CREATE INDEX ref_idx ON NGRAMS (ref);
CREATE INDEX ngram_idx ON NGRAMS (ngram);

Для быстрой вставки с использованием массовых восходящих событий, которыепомечены как удаленные, вставлены с null для столбца данных таблицы NGRAM_CONTENT, и никакие внешние ограничения не были установлены, однако и id, и ref из таблицы ngrams являются внешними ключами для таблицы NGRAM_CONTENT.

Некоторые примеры данных

Ngram_Content:
|id | ref  | data       |
| 1 | 'P1' | some_json  |
| 2 | 'P1' | some_new_json  | # P1 comes again as an update
| 3 | 'P2' | P3  | 
| 4 | 'P1' | null  | 

Ngrams: 

name_length | ngram | ref  | id |
12          | CH    | 'P1' | 1  |
12          | AN    | 'P1' | 1  |
14          | NEW   | 'P1' | 2  |
20          | CH    | 'P2' | 3  |
20          | CHAI  | 'P2' | 3  |
...

Для приведенных выше данных, если я найду нграммы 'CH' или 'AN' с идентификатором <= 1, тогда он вернет <code>P1 с содержанием some_json однакоесли я буду искать с идентификатором <= 2, то он не будет совпадать, так как последний номер в <code>id=2 был обновлен до NEW, и если я буду искать NEW с идентификатором <= 5, он вернетсяничего, так как последний <code>P1 имеет бEEN удалено.

Все поиски должны быть выполнены на расстоянии name_length от и до.

Другими словами, только найти самое последнее содержание ngram для данного ref, который имеетне был удален до определенного id в пределах name_length

Есть два условия, которые мне нужно поддерживать 1. С идентификатором события (для исторических прогонов) 2. Без использования идентификатора события использованиесамый последний

Итак, я придумал 2 варианта, как это

С event_id:

select w.* From NGRAM_CONTENT  w
inner join (
    select max(w.id) as w_max_event_id, w.ref from NGRAMS w
    inner join (
            select max(id) as max_event_id, ref from NGRAMS  where
                name_length between a_number and b_number AND ngram in ('YU', 'CA', 'SAN', 'LT', 'TO', etc) AND id < an_event_id group by ref having count(ref) >= a_threshold) i
            on w.ref = i.ref where w.id >= i.max_event_id AND w.id < an_event_id group by w.ref) wi
    on w.ref = wi.ref and w.event_id = wi.w_max_event_id where w.data is not null;

Без event_id:

select w.* From NGRAM_CONTENT  w
inner join (
    select max(w.id) as w_max_event_id, w.ref from NGRAMS w
    inner join (
            select max(id) as max_event_id, ref from NGRAMS  where
                name_length between a_number and b_number AND ngram in ('YU', 'CA', 'SAN', 'LT', 'TO', etc) group by ref having count(ref) >= a_threshold) i
            on w.ref = i.ref where w.id >= i.max_event_id group by w.ref) wi
    on w.ref = wi.ref and w.event_id = wi.w_max_event_id where w.data is not null;

Оба запросаПостроение занимает много времени, и при выполнении объяснения запроса Postgres отображается как полное сканирование.

SEQ_SCAN (Seq Scan)  table: NGAMS;  121494200   3358896.0   0.0 Node Type = Seq Scan;
Parent Relationship = Outer;
Parallel Aware = true;
Relation Name = NGRAMS;
Alias = w_1;
Startup Cost = 0.0;
Total Cost = 3358896.0;
Plan Rows = 121494200;
Plan Width = 16;

Подробный план выполнения с execute (analyze, buffers) query

 Nested Loop  (cost=5032852.92..6943974.42 rows=1 width=381) (actual time=50787.356..52095.938 rows=9437 loops=1)
   Buffers: shared hit=149882 read=769965, temp read=732 written=736
   ->  Finalize GroupAggregate  (cost=5032852.35..5125447.71 rows=265783 width=16) (actual time=50785.079..50808.811 rows=9437 loops=1)
         Group Key: w_1.ref
         Buffers: shared hit=114072 read=758535, temp read=732 written=736
         ->  Gather Merge  (cost=5032852.35..5120132.05 rows=531566 width=16) (actual time=50785.072..50801.624 rows=10261 loops=1)
               Workers Planned: 2
               Workers Launched: 2
               Buffers: shared hit=343724 read=2276169, temp read=2196 written=2208
               ->  Partial GroupAggregate  (cost=5031852.33..5057776.12 rows=265783 width=16) (actual time=50766.172..50777.757 rows=3420 loops=3)
                     Group Key: w_1.ref
                     Buffers: shared hit=343724 read=2276169, temp read=2196 written=2208
                     ->  Sort  (cost=5031852.33..5039607.65 rows=3102128 width=16) (actual time=50766.163..50769.734 rows=41777 loops=3)
                           Sort Key: w_1.ref
                           Sort Method: quicksort  Memory: 3251kB
                           Worker 0:  Sort Method: quicksort  Memory: 3326kB
                           Worker 1:  Sort Method: quicksort  Memory: 3396kB
                           Buffers: shared hit=343724 read=2276169, temp read=2196 written=2208
                           ->  Hash Join  (cost=787482.50..4591332.06 rows=3102128 width=16) (actual time=14787.585..50749.022 rows=41777 loops=3)
                                 Hash Cond: (w_1.ref = i.ref)
                                 Join Filter: (w_1.id >= i.max_event_id)
                                 Buffers: shared hit=343708 read=2276169, temp read=2196 written=2208
                                 ->  Parallel Seq Scan on NGRAMS w_1  (cost=0.00..3662631.50 rows=53797008 width=16) (actual time=0.147..30898.313 rows=38518899 loops=3)
                                       Filter: (id < 45000000)
                                       Rows Removed by Filter: 58676466
                                       Buffers: shared hit=15819 read=2128135
                                 ->  Hash  (cost=786907.78..786907.78 rows=45978 width=16) (actual time=14767.179..14767.180 rows=9437 loops=3)
                                       Buckets: 65536  Batches: 1  Memory Usage: 955kB
                                       Buffers: shared hit=327861 read=148034, temp read=2196 written=2208
                                       ->  Subquery Scan on i  (cost=782779.42..786907.78 rows=45978 width=16) (actual time=14669.187..14764.701 rows=9437 loops=3)
                                             Buffers: shared hit=327861 read=148034, temp read=2196 written=2208
                                             ->  GroupAggregate  (cost=782779.42..786448.00 rows=45978 width=16) (actual time=14669.186..14763.369 rows=9437 loops=3)
                                                   Group Key: NGRAMS.ref
                                                   Filter: (count(NGRAMS.ref) >= 2)
                                                   Rows Removed by Filter: 210038
                                                   Buffers: shared hit=327861 read=148034, temp read=2196 written=2208
                                                   ->  Sort  (cost=782779.42..783265.52 rows=194442 width=16) (actual time=14669.164..14708.948 rows=229489 loops=3)
                                                         Sort Key: NGRAMS.ref
                                                         Sort Method: external merge  Disk: 5856kB
                                                         Worker 0:  Sort Method: external merge  Disk: 5856kB
                                                         Worker 1:  Sort Method: external merge  Disk: 5856kB
                                                         Buffers: shared hit=327861 read=148034, temp read=2196 written=2208
                                                         ->  Index Only Scan using combined_index on NGRAMS  (cost=0.57..762373.68 rows=194442 width=16) (actual time=0.336..14507.098 rows=229489 loops=3)
                                                               Index Cond: ((indexed = ANY ('{YU,CA,SAN,LT,TO}'::text[])) AND (name_length >= 15) AND (name_length <= 20) AND (event_id < 45000000))
                                                               Heap Fetches: 688467
                                                               Buffers: shared hit=327861 read=148034
   ->  Index Scan using idx_id_ngram_content on NGRAM_CONTENT w  (cost=0.56..6.82 rows=1 width=381) (actual time=0.135..0.136 rows=1 loops=9437)
         Index Cond: (id = (max(w_1.id)))
         Filter: ((data IS NOT NULL) AND (w_1.ref = ref))
         Buffers: shared hit=35810 read=11430
 Planning Time: 12.075 ms
 Execution Time: 52100.064 ms

Есть лиспособ сделать эти запросы быстрее?

Я попытался разбить запрос на более мелкие куски и проанализировать их, и обнаружил, что полное сканирование происходит из этого объединения

select max(w.id) as w_max_event_id, w.ref from NGRAMS w
    inner join (
            select max(event_id) as max_event_id, ref from NGRAMS  where
                name_length between a_number and b_number AND ngram in ('YU', 'CA', 'SAN', 'LT', 'TO', etc) AND id < an_event_id group by ref having count(ref) >= a_threshold) i
            on w.ref = i.ref where w.id >= i.max_event_id AND w.id < an_event_id group by w.ref

, но я не знаю почему и не знаю, какие индексыотсутствуют.

Желательно, чтобы ответ был для Postgres, но в худшем случае, пожалуйста, предоставьте ответ и для Oracle.

Я знаю, что это долго, но, пожалуйста, постарайтесь помочь, если можете. Спасибо

1 Ответ

0 голосов
/ 04 октября 2019

С такими различными запросами лучше всего создать три индекса:

CREATE INDEX ON ngrams (id);
CREATE INDEX ON ngrams (name_length);
CREATE INDEX ON ngrams (ngram);

и надеяться, что PostgreSQL может использовать Bitmap And , если не выполняется одно из условийдостаточно избирательно.

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