sql >> Base de Datos >  >> RDS >> PostgreSQL

Optimización de la consulta de conteo para PostgreSQL

PostgreSQL en realidad admite índices GIN en columnas de matriz. Desafortunadamente, no parece ser útil para NOT ARRAY[...] <@ indexed_col y GIN de todos modos, los índices no son adecuados para las tablas que se actualizan con frecuencia.

Demostración:

CREATE TABLE arrtable (id integer primary key, array_column integer[]);

INSERT INTO arrtable(1, ARRAY[1,2,3,4]);

CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);

-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;

explain (buffers, analyze) select count(id) 
from arrtable 
where not (ARRAY[1] <@ arrtable.array_column);

Desafortunadamente, esto muestra que, tal como está escrito, no podemos usar el índice. Si no niega la condición, se puede usar, por lo que puede buscar y contar filas que hacen contener el elemento de búsqueda (eliminando NOT ).

Podría usar el índice para contar las entradas que hacen contener el valor objetivo, luego restar ese resultado de un recuento de todas las entradas. Desde count ing todas las filas de una tabla es bastante lento en PostgreSQL (9.1 y anteriores) y requiere un escaneo secuencial, en realidad será más lento que su consulta actual. Es posible que en 9.2 se pueda usar un escaneo de solo índice para contar las filas si tiene un índice de árbol b en id , en cuyo caso esto podría estar bien:

SELECT (
  SELECT count(id) FROM arrtable
) - (
  SELECT count(id) FROM arrtable 
  WHERE (ARRAY[1] <@ arrtable.array_column)
);

Se garantiza que funcionará peor que su versión original para Pg 9.1 e inferior, porque además del escaneo de secuencias, su original lo requiere también necesita un escaneo de índice GIN. Ahora probé esto en 9.2 y parece usar un índice para el conteo, por lo que vale la pena explorarlo para 9.2. Con algunos datos ficticios menos triviales:

drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);

Tenga en cuenta que un índice GIN como este ralentizará MUCHO las actualizaciones y, en primer lugar, es bastante lento de crear. No es adecuado para tablas que se actualizan mucho, como su tabla.

Peor aún, la consulta que utiliza este índice tarda hasta el doble de tiempo que la consulta original y, en el mejor de los casos, la mitad. en el mismo conjunto de datos. Es peor para los casos en los que el índice no es muy selectivo como ARRAY[1] - 4s vs 2s para la consulta original. Cuando el índice es muy selectivo (es decir, no hay muchas coincidencias, como ARRAY[199] ) se ejecuta en aproximadamente 1,2 segundos frente a los 3 segundos del original. Simplemente no vale la pena tener este índice para esta consulta.

¿La lección aquí? A veces, la respuesta correcta es simplemente hacer un escaneo secuencial.

Dado que eso no servirá para sus tasas de aciertos, mantenga una vista materializada con un disparador como sugiere @debenhur, o intente invertir la matriz para que sea una lista de parámetros que la entrada no tiene para que pueda usar un índice GiST como sugiere @maniek.