Eu tenho uma consulta como esta:
SELECT t0.id
FROM platform_conversations t0
LEFT OUTER JOIN contacts t1 ON t1.id = t0.contact_id
WHERE t0.user_id = 5340
AND (
t0.participant ILIKE '%baa%' -- (1)
OR t1.first_name ILIKE '%baa%' -- (2)
)
LIMIT 50;
e
CREATE INDEX ix_conversations_participant
ON platform_conversations USING GIN (participant gin_trgm_ops);
CREATE INDEX ix_trgm_contacts_search
ON contacts USING GIN (first_name gin_trgm_ops);
E não consigo descobrir por que os índices não são usados com OR
condição. Se eu usar apenas (1), ou apenas (2), ou usar AND
em vez disso, ambos serão usados.
Aqui está o plano:
Por apenas (1)
Limit (cost=12.43..203.68 rows=50 width=37) (actual time=0.037..0.037 rows=0 loops=1)
-> Bitmap Heap Scan on platform_conversations t0 (cost=12.43..222.80 rows=55 width=37) (actual time=0.030..0.030 rows=0 loops=1)
Recheck Cond: ((participant)::text ~~* '%baa%'::text)
Filter: (user_id = 5340)
-> Bitmap Index Scan on ix_conversations_participant (cost=0.00..12.42 rows=55 width=0) (actual time=0.012..0.012 rows=0 loops=1)
Index Cond: ((participant)::text ~~* '%baa%'::text)
Planning time: 0.397 ms
Execution time: 0.092 ms
Por apenas (2)
Limit (cost=16.71..446.06 rows=50 width=37) (actual time=0.034..0.034 rows=0 loops=1)
-> Nested Loop (cost=16.71..471.82 rows=53 width=37) (actual time=0.028..0.028 rows=0 loops=1)
-> Bitmap Heap Scan on contacts t1 (cost=16.29..158.99 rows=37 width=37) (actual time=0.022..0.022 rows=0 loops=1)
Recheck Cond: ((first_name)::text ~~* '%baa%'::text)
-> Bitmap Index Scan on ix_trgm_contacts_search (cost=0.00..16.28 rows=37 width=0) (actual time=0.016..0.016 rows=0 loops=1)
Index Cond: ((first_name)::text ~~* '%baa%'::text)
-> Index Scan using ix_platform_conversations_contact_id on platform_conversations t0 (cost=0.42..8.45 rows=1 width=74) (never executed)
Index Cond: ((contact_id)::text = (t1.id)::text)
Filter: (user_id = 5340)
Planning time: 0.840 ms
Execution time: 0.121 ms
Um lento (com OR):
Limit (cost=25771.43..47419.63 rows=50 width=37) (actual time=6652.292..6652.292 rows=0 loops=1)
-> Hash Left Join (cost=25771.43..72531.55 rows=108 width=37) (actual time=6652.282..6652.282 rows=0 loops=1)
Hash Cond: ((t0.contact_id)::text = (t1.id)::text)
Filter: (((t0.participant)::text ~~* '%baa%'::text) OR ((t1.first_name)::text ~~* '%baa%'::text))
Rows Removed by Filter: 553477
-> Seq Scan on platform_conversations t0 (cost=0.00..20627.29 rows=553512 width=87) (actual time=0.045..1741.899 rows=553477 loops=1)
Filter: (user_id = 5340)
Rows Removed by Filter: 386
-> Hash (cost=17578.19..17578.19 rows=384819 width=46) (actual time=2372.508..2372.508 rows=384819 loops=1)
Buckets: 65536 Batches: 16 Memory Usage: 2359kB
-> Seq Scan on contacts t1 (cost=0.00..17578.19 rows=384819 width=46) (actual time=0.014..1168.218 rows=384819 loops=1)
Planning time: 0.741 ms
Execution time: 6652.333 ms
Não pode apenas fazer varreduras de índice de bitmap em t1, t2 e, em seguida, ORs de bitmap neles? Por que o OR é um problema tão grande?
Postgres 9.6 (não é possível criar tag, não há representante suficiente)
A pergunta é: " Você deseja filtrar os dados DEPOIS de fazer a junção? OU Deseja filtrar os dados ANTES de fazer a junção? "
Por causa da falta de dados ao usar o
OR
. É por isso que o PG precisa fazer um trabalhosequence scan
nessas mesas.Sua condição é
t1.id = t2.contract_id, (1) or (2)
.No seu caso, suponha que o PG usará
bitmap
index, ou seja,t1
usa(1)
et2
usa(2)
. Aqui, você pode ver se(1)
é falso (!), então verifique(2)
. O problema é que se(2)
for verdade, faltarão dados das colunas de(1)
.(!)
Se o PG realmente usabitmap
índice, esse caso nunca ocorreu (porque (1) é sempre verdadeiro) .Aqui, meu exemplo simples (Postgres 9.6) sobre a junção AFTER-BEFORE:
ATUALIZAÇÃO de Kadek
Isso é ótimo, a união é uma boa escolha. Com base em sua consulta, você pode criar uma nova
first_name
coluna naplatform_conversations
tabela e, em seguida, criar um índice para duas colunasfirst_name
eparticipant
. Claro, você deve equilibrar entre o fluxo de gravação e leitura.