¿Cómo indexar una consulta con `WHERE field IS NULL`?

14

Tengo una tabla con muchas inserciones, estableciendo uno de los campos ( uploaded_at) en NULL. Luego, una tarea periódica selecciona todas las tuplas WHERE uploaded_at IS NULL, las procesa y actualiza, estableciendo uploaded_atla fecha actual.

¿Cómo debo indexar la tabla?

Entiendo que debería usar un índice parcial como:

CREATE INDEX foo ON table (uploaded_at) WHERE uploaded_at IS NULL

O algo así. Sin embargo, estoy un poco confundido si es correcto indexar en un campo que siempre lo es NULL. O si es correcto usar un índice b-tree. Hash parece una mejor idea, pero es obsoleto y no se replica a través de la replicación de espera en caliente. Cualquier consejo sería muy apreciado.

He experimentado un poco con los siguientes índices:

"foo_part" btree (uploaded_at) WHERE uploaded_at IS NULL
"foo_part_id" btree (id) WHERE uploaded_at IS NULL

y el planificador de consultas parece elegir siempre el foo_partíndice. explain analyseTambién produce un resultado ligeramente mejor para el foo_partíndice:

Index Scan using foo_part on t1  (cost=0.28..297.25 rows=4433 width=16) (actual time=0.025..3.649 rows=4351 loops=1)
   Index Cond: (uploaded_at IS NULL)
 Total runtime: 4.060 ms

vs

Bitmap Heap Scan on t1  (cost=79.15..6722.83 rows=4433 width=16) (actual time=1.032..4.717 rows=4351 loops=1)
   Recheck Cond: (uploaded_at IS NULL)
   ->  Bitmap Index Scan on foo_part_id  (cost=0.00..78.04 rows=4433 width=0) (actual time=0.649..0.649 rows=4351 loops=1)
 Total runtime: 5.131 ms
Kirill Zaitsev
fuente

Respuestas:

10

En este caso especial, la columna realmente indexada es irrelevante para la consulta en cuestión. Puedes elegir cualquier columna. Elegiría algo más que uploaded_at, que es inútil. Alguna columna que puede ser útil para otras consultas y no es más grande que 8 bytes, idealmente.

CREATE INDEX foo ON table bar (some_col) WHERE uploaded_at IS NULL;

Si no tiene un caso de uso para ninguna otra columna, es mejor quedarse con lo inútil uploaded_at, para no introducir costos de mantenimiento adicionales para el índice y restricciones para las actualizaciones HOT. Más:

O use una constante como expresión de índice si no tiene uso para ninguna otra columna de índice. Me gusta:

CREATE INDEX baz ON table bar ((TRUE)) WHERE uploaded_at IS NULL;

Paréntesis requeridos. Esto también mantiene el índice en tamaño mínimo. Pero aunque la columna de índice nunca es mayor de 8 bytes (que es el caso timestamp) todavía tiene un tamaño mínimo de todos modos. Relacionado:

Erwin Brandstetter
fuente
¿Podría ser un idcampo en serie , por ejemplo?
Kirill Zaitsev
1
@teferi: a seriales tan bueno como cualquiera. El punto es si realmente hay consultas para usarlo.
Erwin Brandstetter