Postgres ненормальное поведение движка на атрибутах поля jsonb - PullRequest
2 голосов
/ 25 мая 2020

Привет, у меня есть этот запрос на postgres 12

select
   count(*)
from
    modulo730.ods_t_record as r
left join(
    select
        *
    from
        modulo730.ods_t_ire as oti
    where
        cod_azienda = '080239'
        and not cod_scenario = 'CUPDT' ) B on
    r.cod_evento = B.cod_evento
where
  (r.uid_task = '41b7c8ea-4783-4f60-b3f2-980777b86693' )

 and  r.cod_azienda = '080239'
   and r.rec_aggiornato ->> 'annoDocumento' = '2020'

Эта первая версия запроса ведет себя так, как я ожидал, он использует раздел поля cod_azienda Он выполняет сканирование индекса растрового изображения на uid_task и в поле annoDocumento, которое является частью json. Счетчик занимает 350 мс. Теперь, если я добавлю еще одно условие в json, я найду последнюю часть, подобную этой

  ...
        and r.rec_aggiornato ->> 'annoDocumento' = '2020'
        and ( r.rec_aggiornato ->> 'tipo_documento' = 'FT' )

БД становится неспособным выполнить запрос, даже не возвращается. То есть только с одним из условий (он также работает путем инвертирования условий) он выполняет его правильно, если я их объединяю ... БУМ

Есть предложения? Спасибо

Это план

Gather  (cost=34356.37..34356.48 rows=1 width=8)
          Workers Planned: 1
          Single Copy: true
          ->  Aggregate  (cost=33356.37..33356.38 rows=1 width=8)
                ->  Nested Loop Left Join  (cost=1410.54..33356.37 rows=1 width=0)
                      ->  Append  (cost=1410.54..18597.25 rows=1 width=18)
                            ->  Bitmap Heap Scan on ods_t_record_mch (cost=1410.54..18597.24 rows=1 width=18)
                                  Recheck Cond: (((cod_azienda)::text = '080239'::text) AND ((uid_task)::text = '41b7c8ea-4783-4f60-b3f2-980777b86693'::text))
                                  Filter: (((rec_aggiornato ->> 'annoDocumento'::text) = '2020'::text) AND ((rec_aggiornato ->> 'tipo_documento'::text) = 'FT'::text))
                                  ->  Bitmap Index Scan on ods_t_record_mch_cod_azienda_uid_task_idx  (cost=0.00..1410.54 rows=14612 width=0)
                                        Index Cond: (((cod_azienda)::text = '080239'::text) AND ((uid_task)::text = '41b7c8ea-4783-4f60-b3f2-980777b86693'::text))
                      ->  Append  (cost=0.00..14759.11 rows=1 width=18)
                            ->  Seq Scan on ods_t_ire_mch oti  (cost=0.00..14759.10 rows=1 width=18)
                                  Filter: (((cod_scenario)::text <> 'CUPDT'::text) AND ((cod_azienda)::text = '080239'::text) AND ((r.cod_evento)::text = (cod_evento)::text))

Это скрипт первой таблицы

CREATE TABLE modulo730.ods_t_record (
    uid varchar(36) NOT NULL,
    rec_ori jsonb NOT NULL,
    insert_time timestamp NOT NULL,
    inserter varchar(36) NOT NULL,
    modify_time timestamp NULL,
    modifier varchar NULL,
    stato int4 NOT NULL DEFAULT 0,
    uid_task varchar(36) NOT NULL,
    cod_evento varchar(128) NOT NULL,
    uid_mbi_t_record varchar(36) NOT NULL,
    cod_propretario varchar(128) NULL,
    dat_evento timestamp NULL,
    cod_azienda varchar(128) NOT NULL,
    tip_evento varchar(128) NOT NULL,
    cod_scenario varchar(128) NOT NULL,
    flg_inviato_mef varchar(1) NULL DEFAULT 'N'::character varying,
    flg_rre_mef varchar(1) NULL DEFAULT 'N'::character varying,
    rec_aggiornato jsonb NULL,
    flg_modo varchar(1) NULL,
    flg_inviato_reg varchar(1) NULL,
    flg_rre_reg varchar(1) NULL,
    flg_inviato_dwh varchar(1) NULL,
    rec_hash text NULL,
    stato_classificazione varchar(50) NULL,
    flg_classificazione varchar(1) NULL,
    CONSTRAINT ods_t_record_key PRIMARY KEY (cod_azienda, tip_evento, cod_scenario, cod_evento)
)
PARTITION BY LIST (cod_azienda);
CREATE INDEX idx_ods_t_record_cliente ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'cod_cliente_competenza'::text)));
CREATE INDEX idx_ods_t_record_dat_reg ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'data_registrazione'::text)));
CREATE INDEX idx_ods_t_record_dat_ult_inc ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'dataUltimoIncasso'::text)));
CREATE INDEX idx_ods_t_record_des_reason ON ONLY modulo730.ods_t_record USING btree (cod_azienda, (((rec_aggiornato -> 'classificationReason'::text) ->> 'des_reason'::text)));
CREATE INDEX idx_ods_t_record_doc_coll ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'nr_Documento_Collegamento'::text)));
CREATE INDEX idx_ods_t_record_evsan ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'codEventoSanitario'::text)));
CREATE INDEX idx_ods_t_record_note_pres ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'note_prestazione'::text)));
CREATE INDEX idx_ods_t_record_nr_doc ON ONLY modulo730.ods_t_record USING btree (cod_azienda, ((rec_aggiornato ->> 'nr_Documento'::text)));
CREATE UNIQUE INDEX ods_t_record_cod_azienda_idx ON ONLY modulo730.ods_t_record USING btree (cod_azienda, uid);
CREATE INDEX ods_t_record_cod_propretario_idx ON ONLY modulo730.ods_t_record USING btree (cod_azienda, cod_propretario);
CREATE INDEX ods_t_record_uid_task_idx ON ONLY modulo730.ods_t_record USING btree (cod_azienda, uid_task);

второй таблицы

CREATE TABLE modulo730.ods_t_ire (
    uid varchar(36) NOT NULL,
    inserter varchar(128) NULL,
    insert_time timestamp NULL,
    modifier varchar(128) NULL,
    modify_time timestamp NULL,
    uid_task varchar(36) NULL,
    cod_evento varchar(128) NOT NULL,
    cod_proprietario varchar(128) NULL,
    cod_azienda varchar(128) NOT NULL,
    tip_evento varchar(128) NOT NULL,
    cod_scenario varchar(128) NOT NULL,
    data_invio timestamp NULL,
    flg_inviato varchar(1) NULL,
    flg_ritorno varchar(1) NULL,
    rec_inviato_ori jsonb NULL,
    rec_inviato text NULL,
    rec_ritorno text NULL,
    uid_sent_file varchar(36) NULL,
    flow_state varchar(50) NULL,
    cod_error varchar NULL,
    cod_evento_inviato varchar(128) NULL,
    dat_evento timestamp NULL,
    cod_evento_collegato varchar NULL,
    dat_evento_collegato timestamp NULL,
    ticket varchar NULL,
    flg_cancellazione varchar NULL,
    ticket_cancellazione varchar NULL,
    cod_destinatario varchar NOT NULL,
    CONSTRAINT ods_t_ire_pk PRIMARY KEY (cod_azienda, tip_evento, cod_scenario, cod_destinatario, cod_evento)
)
PARTITION BY LIST (cod_azienda);
CREATE UNIQUE INDEX ods_t_ire_cod_azienda_idx ON ONLY modulo730.ods_t_ire USING btree (cod_azienda, uid);

Как дальнейшее указание ниже, равнина, учитывающая только одно условие для атрибутов json, является атрибутами с низкой мощностью. Я не эксперт, но вижу, что в более быстром случае он выполняет своего рода параллельное вычисление

Finalize Aggregate  (cost=33423.14..33423.15 rows=1 width=8)
  ->  Gather  (cost=33422.93..33423.14 rows=2 width=8)
        Workers Planned: 2
        ->  Partial Aggregate  (cost=32422.93..32422.94 rows=1 width=8)
              ->  Parallel Hash Left Join  (cost=15479.31..32422.85 rows=30 width=0)
                    Hash Cond: ((r.cod_evento)::text = (oti.cod_evento)::text)
                    ->  Parallel Append  (cost=1410.56..18353.88 rows=30 width=18)
                          ->  Parallel Bitmap Heap Scan on ods_t_record_mch r  (cost=1410.56..18353.73 rows=30 width=18)
                                Recheck Cond: (((cod_azienda)::text = '080239'::text) AND ((uid_task)::text = '41b7c8ea-4783-4f60-b3f2-980777b86693'::text))
                                Filter: ((rec_aggiornato ->> 'tipo_documento'::text) = 'FT'::text)
                                ->  Bitmap Index Scan on ods_t_record_mch_cod_azienda_uid_task_idx  (cost=0.00..1410.54 rows=14612 width=0)
                                      Index Cond: (((cod_azienda)::text = '080239'::text) AND ((uid_task)::text = '41b7c8ea-4783-4f60-b3f2-980777b86693'::text))
                    ->  Parallel Hash  (cost=13160.38..13160.38 rows=72669 width=18)
                          ->  Parallel Append  (cost=0.00..13160.38 rows=72669 width=18)
                                ->  Parallel Seq Scan on ods_t_ire_mch oti  (cost=0.00..12797.04 rows=72669 width=18)
                                      Filter: (((cod_scenario)::text <> 'CUPDT'::text) AND ((cod_azienda)::text = '080239'::text))
...