Ho una query relativamente semplice su una tabella con 1,5 milioni di righe:
SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
produzione:
Limit (cost=8.84..12.86 rows=1 width=8) (actual time=0.985..0.986 rows=1 loops=1) -> Bitmap Heap Scan on publication (cost=8.84..12.86 rows=1 width=8) (actual time=0.984..0.985 rows=1 loops=1) Recheck Cond: ((mtid = 9762715) OR (last_modifier = 21321)) -> BitmapOr (cost=8.84..8.84 rows=1 width=0) (actual time=0.971..0.971 rows=0 loops=1) -> Bitmap Index Scan on publication_pkey (cost=0.00..4.42 rows=1 width=0) (actual time=0.295..0.295 rows=1 loops=1) Index Cond: (mtid = 9762715) -> Bitmap Index Scan on publication_last_modifier_btree (cost=0.00..4.42 rows=1 width=0) (actual time=0.674..0.674 rows=0 loops=1) Index Cond: (last_modifier = 21321) Total runtime: 1.027 ms
Fin qui tutto bene, veloce e utilizza gli indici disponibili.
Ora, se modifico un po 'una query, il risultato sarà:
SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;
L' EXPLAIN ANALYZE
output è:
Limit (cost=0.01..2347.74 rows=5000 width=8) (actual time=2735.891..2841.398 rows=1 loops=1) -> Seq Scan on publication (cost=0.01..349652.84 rows=744661 width=8) (actual time=2735.888..2841.393 rows=1 loops=1) Filter: ((hashed SubPlan 1) OR (last_modifier = 21321)) SubPlan 1 -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1) Total runtime: 2841.442 ms
Non così veloce e usando seq scan ...
Naturalmente, la query originale eseguita dall'applicazione è un po 'più complessa e persino più lenta, e ovviamente l'originale generato dall'ibernazione non lo è (SELECT 9762715)
, ma la lentezza è presente anche per questo (SELECT 9762715)
! La query viene generata da ibernazione, quindi è piuttosto una sfida modificarli e alcune funzionalità non sono disponibili (ad es. UNION
Non è disponibile, il che sarebbe veloce).
Le domande
- Perché non è possibile utilizzare l'indice nel secondo caso? Come potrebbero essere usati?
- Posso migliorare le prestazioni della query in qualche altro modo?
Pensieri aggiuntivi
Sembra che potremmo usare il primo caso facendo manualmente un SELECT, quindi inserendo l'elenco risultante nella query. Anche con 5000 numeri nell'elenco IN () è quattro volte più veloce della seconda soluzione. Tuttavia, sembra proprio SBAGLIATO (inoltre, potrebbe essere 100 volte più veloce :)). È del tutto incomprensibile il motivo per cui il pianificatore di query utilizza un metodo completamente diverso per queste due query, quindi vorrei trovare una soluzione migliore a questo problema.
(SELECT 9762715)
.
(SELECT 9762715)
. Alla domanda di ibernazione: potrebbe essere fatto, ma richiede una seria riscrittura del codice, poiché abbiamo query di criteri di ibernazione definite dall'utente che vengono tradotte al volo. Quindi essenzialmente dovremmo modificare l'ibernazione che è un'impresa enorme con molti possibili effetti collaterali.
JOIN
invece delIN ()
? Inoltre, èpublication
stato analizzato di recente?