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.