Scansione Seq imprevista durante l'esecuzione di query su valori booleani con valore NULL


10

Ho una colonna di database chiamata auto_reviewdove si trova il tipo di colonna boolean. C'è un indice per quel campo, creato usando l'ORM ActiveRecord.

CREATE INDEX index_table_on_auto_renew ON table USING btree (auto_renew);

Quando eseguo una query nel campo per un valore booleano, PG utilizza l'indice come previsto.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" = 'f'
                                          QUERY PLAN
----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on table  (cost=51.65..826.50 rows=28039 width=186)
   Filter: (NOT auto_renew)
   ->  Bitmap Index Scan on index_domains_on_auto_renew  (cost=0.00..44.64 rows=2185 width=0)
         Index Cond: (auto_renew = false)
(4 rows)

Quando il valore è NULL, viene utilizzata una scansione sequenziale.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" IS NULL
                           QUERY PLAN
----------------------------------------------------------------
 Seq Scan on table  (cost=0.00..1094.01 rows=25854 width=186)
   Filter: (auto_renew IS NULL)
(2 rows)

Sono curioso di sapere il motivo dietro questa scelta.

Risposte:


19

In generale, col IS NULLè un possibile candidato per una ricerca (predefinita) dell'indice b-tree. Il manuale :

Inoltre, una condizione IS NULLo IS NOT NULLsu una colonna di indice può essere utilizzata con un indice B-tree.

Per ottenere la prova, disabilita le scansioni sequenziali (solo in una sessione di test!):

SET enable_seqscan = OFF;

Cito il manuale qui :

enable_seqscan (boolean)

Abilita o disabilita l'uso del pianificatore di query di tipi di piano di scansione sequenziale. È impossibile sopprimere completamente le scansioni sequenziali, ma la disattivazione di questa variabile scoraggia il pianificatore dall'utilizzarne una se sono disponibili altri metodi. L'impostazione predefinita è attiva.

Quindi riprovare:

EXPLAIN ANALYZE SELECT * FROM tbl WHERE auto_renew IS NULL;

Ciò comporterà probabilmente una scansione dell'indice bitmap più lenta di una scansione sequenziale sulla tabella.

Ripristina o chiudi la sessione (l'impostazione è session-local).

RESET enable_seqscan;

Gli indici sulle booleancolonne sono utili solo in alcuni casi. Il pianificatore utilizza un indice solo se si aspetta che sia più veloce. I calcoli si basano sulle impostazioni dei costi e sulle statistiche raccolte da ANALYZE. Se una parte considerevole della tabella corrisponde alla tua condizione (circa il 5% o più, dipende), in genere è più veloce eseguire una scansione completa della tabella.

Questo lascia il raro valore in una booleancolonna come unico candidato utile per un indice semplice. Ed è in genere più efficiente creare un indice parziale (più specializzato) per questo, che è più economico da mantenere, più piccolo, più veloce e utilizzato più facilmente se le condizioni della query corrispondono.

Se hai molte query con le quali cerchi delle righe auto_renew IS NULLe il NULLcaso non è molto comune (e / o hai bisogno di un certo ordinamento), questo indice ti aiuterebbe a trovare / ordinare rapidamente queste righe:

CREATE INDEX index_tbl_tbl_id_auto_renew_null ON tbl (tbl_id)
WHERE auto_renew IS NULL;

La condizione dell'indice parziale deve essere ripetuta WHEREpiù o meno esattamente nella clausola di una query per fare in modo che il pianificatore di query realizzi che l'indice è applicabile.

La colonna indicizzata ( tbl_id) è una scelta arbitraria. La parte importante è la WHEREclausola. Questo particolare indice sarebbe più efficace per le query con ORDER BY tbl_ido un filtro aggiuntivo o su cui partecipare tbl_id. Potresti renderlo un indice a più colonne . Le colonne booleane sono spesso più utili in combinazione con altre.

A parte: gli ORM sono stampelle che non riescono regolarmente a sfruttare appieno il tuo RDBMS.


Risposta brillante, grazie Erwin. Sono triste di non poterlo votare due volte.
Simone Carletti,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.