PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Otimização da consulta de contagem para PostgreSQL


Na verdade, o PostgreSQL suporta índices GIN em colunas de array. Infelizmente, não parece ser utilizável para NOT ARRAY[...] <@ indexed_col e GIN índices são inadequados para tabelas atualizadas com freqüência de qualquer maneira.

Demonstração:
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);

Infelizmente, isso mostra que, conforme escrito, não podemos usar o index. Se você não negar a condição, ela pode ser usada, então você pode pesquisar e contar as linhas que fazem conter o elemento de pesquisa (removendo NOT ).

Você pode usar o índice para contar as entradas que fazem contém o valor de destino e, em seguida, subtraia esse resultado de uma contagem de todas as entradas. Desde count A execução de todas as linhas em uma tabela é bastante lenta no PostgreSQL (9.1 e anterior) e requer uma varredura sequencial, isso será realmente mais lento do que sua consulta atual. É possível que na versão 9.2 uma varredura somente de índice possa ser usada para contar as linhas se você tiver um índice de árvore b em id , caso em que isso pode realmente estar OK:
SELECT (
  SELECT count(id) FROM arrtable
) - (
  SELECT count(id) FROM arrtable 
  WHERE (ARRAY[1] <@ arrtable.array_column)
);

É garantido um desempenho pior do que sua versão original para Pg 9.1 e abaixo, porque além do seqscan seu original requer também precisa de uma verificação de índice GIN. Agora testei isso no 9.2 e parece usar um índice para a contagem, então vale a pena explorar o 9.2. Com alguns dados fictícios menos triviais:
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);

Observe que um índice GIN como esse desacelera muito as atualizações e é bastante lento para criar em primeiro lugar. Não é adequado para tabelas que são muito atualizadas - como sua tabela.

Pior ainda, a consulta que usa esse índice leva até duas vezes mais do que a consulta original e, na melhor das hipóteses, metade do tempo no mesmo conjunto de dados. É pior para casos em que o índice não é muito seletivo como ARRAY[1] - 4s vs 2s para a consulta original. Onde o índice é altamente seletivo (ou seja:não há muitas correspondências, como ARRAY[199] ) ele é executado em cerca de 1,2 segundos contra os 3 segundos do original. Este índice simplesmente não vale a pena ter para esta consulta.

A lição aqui? Às vezes, a resposta certa é apenas fazer uma varredura sequencial.

Como isso não funcionará para suas taxas de acerto, mantenha uma visão materializada com um gatilho como @debenhur sugere ou tente inverter a matriz para ser uma lista de parâmetros que a entrada não tem para que você possa usar um índice GiST como sugere @maniek.