Consulta:
with firstData as (
select r.display_name as counter,
r.createdat,
r.status,
r.id as runid
from run r
inner join customer c on c.id = r.badmasterid
inner join bank b on b.id = c.registerid
where b.displayname in ('abc', 'pqr')
order by r.createdat desc limit 100
),
secondData as (
select fi.runid,
count(1) filter (where action = 'Add' and type = 'Bank') as addcount,
count(1) filter (where action = 'Modify' and type = 'Bank') as modifycount,
count(1) filter (where action = 'Delete' and type = 'Bank') as deletecount,
count(1) filter (where action is null and type = 'Bank') as pendingcount,
count(1) filter (where (status = 'FAILED' or status = 'FAILED') and type = 'Bank') as failcount
from bigTable fi
where fi.runid in
(select runid from firstData limit 100)
group by fi.runid
)
select r1.*,
case when c1.add_count is null then 0 else c1.add_count end,
case when c1.modify_count is null then 0 else c1.modify_count end,
case when c1.delete_count is null then 0 else c1.delete_count end,
case when c1.pending_count is null then 0 else c1.pending_count end,
case when c1.fail_count is null then 0 else c1.fail_count end
from firstData r1
left join secondData c1 on r1.runid = c1.runid
order by r1.createdat;
**Indexs on Table:**
run : id column
customer : id column
bank : id column
bigTable : (id, status, type, runid) columns
Plano de execução:
Sort (cost=839936.19..839939.32 rows=1250 width=116) (actual time=7838.265..7838.275 rows=100 loops=1)
Sort Key: r1.createdat
Sort Method: quicksort Memory: 34kB
CTE result
-> Limit (cost=79.27..79.52 rows=100 width=52) (actual time=1.601..1.615 rows=100 loops=1)
-> Sort (cost=79.27..79.90 rows=250 width=52) (actual time=1.600..1.607 rows=100 loops=1)
Sort Key: br.createdat DESC
Sort Method: top-N heapsort Memory: 35kB
-> Hash Join (cost=2.84..69.72 rows=250 width=52) (actual time=0.101..1.083 rows=2500 loops=1)
Hash Cond: (r.runid = b.id)
-> Seq Scan on run r (cost=0.00..55.00 rows=2500 width=28) (actual time=0.029..0.213 rows=2500 loops=1)
-> Hash (cost=2.79..2.79 rows=4 width=36) (actual time=0.066..0.068 rows=40 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Hash Join (cost=1.27..2.79 rows=4 width=36) (actual time=0.046..0.061 rows=40 loops=1)
Hash Cond: (b.counterid = rm.id)
-> Seq Scan on bank b (cost=0.00..1.40 rows=40 width=12) (actual time=0.011..0.014 rows=40 loops=1)
-> Hash (cost=1.25..1.25 rows=2 width=36) (actual time=0.026..0.027 rows=20 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on customer c (cost=0.00..1.25 rows=2 width=36) (actual time=0.012..0.018 rows=20 loops=1)
Filter: (display_name = ANY ('{abc,pqr}'::text[]))
-> Hash Right Join (cost=839720.50..839792.38 rows=1250 width=116) (actual time=7838.151..7838.230 rows=100 loops=1)
Hash Cond: (fi.runid = r1.runid)
-> HashAggregate (cost=839717.25..839742.25 rows=2500 width=48) (actual time=7836.481..7836.521 rows=100 loops=1)
Group Key: fi.runid
Batches: 1 Memory Usage: 145kB
-> Hash Semi Join (cost=3.25..810917.25 rows=720000 width=31) (actual time=615.639..7395.089 rows=719888 loops=1)
Hash Cond: (fi.runid = firstData.runid)
-> Seq Scan on bigTable fi (cost=0.00..755654.00 rows=18000000 width=31) (actual time=20.792..5093.711 rows=18000000 loops=1)
-> Hash (cost=2.00..2.00 rows=100 width=4) (actual time=0.046..0.047 rows=100 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 12kB
-> Limit (cost=0.00..2.00 rows=100 width=4) (actual time=0.004..0.023 rows=100 loops=1)
-> CTE Scan on firstData (cost=0.00..2.00 rows=100 width=4) (actual time=0.003..0.017 rows=100 loops=1)
-> Hash (cost=2.00..2.00 rows=100 width=76) (actual time=1.660..1.661 rows=100 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 15kB
-> CTE Scan on firstData r1 (cost=0.00..2.00 rows=100 width=76) (actual time=1.603..1.630 rows=100 loops=1)
Planning Time: 4.387 ms
Execution Time: 7838.458 ms
Criando uma configuração de teste:
Vamos tentar a consulta problemática:
Sem nenhum índice, consigo um plano semelhante ao da pergunta, com o seq scan no bigtable. É muito lento.
Mesma consulta:
Isso é cerca de 100x mais rápido. Ele está usando o índice que acabei de criar, mas apenas para encontrar "runid=...". Não está usando as outras colunas, porque o postgres não sabe fazer isso com vários FILTROs agregados. Portanto, um índice simples em (runid) funcionaria igualmente bem.
Se você tiver um índice de várias colunas (id, status, tipo, runid), será inútil porque runid é a última coluna. além disso, com id sendo a primeira coluna e único, esse índice multicolunas não pode fazer muito mais do que um índice apenas (id), que você já possui se for a chave primária, a menos que você o esteja usando para algo muito específico Acho que pode ser removido.
Agora, a razão pela qual criei esses dois índices é para realmente usá-los:
Desta vez, ele está usando varreduras apenas de índice para tudo, e também nos livramos dos HashAggregates, então é 10x mais rápido que o anterior, para uma aceleração total de cerca de 1000x.
Mas precisa de dois índices multicolunas, que consomem recursos. Você pode usar esses índices para outras consultas. O anterior só precisa de um índice menor no runid (ou um dos índices multicolunas).