O PostgreSQL pode indexar colunas da matriz?

144

Não consigo encontrar uma resposta definitiva para esta pergunta na documentação. Se uma coluna for do tipo matriz, todos os valores inseridos serão indexados individualmente?

Criei uma tabela simples com uma int[]coluna e coloquei um índice exclusivo. Percebi que não era possível adicionar a mesma matriz de entradas, o que me leva a acreditar que o índice é um composto dos itens da matriz, não um índice de cada item.

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

O índice está ajudando esta consulta?

IamIC
fonte

Respostas:

181

Sim, você pode indexar uma matriz, mas é necessário usar os operadores da matriz e o tipo de índice GIN .

Exemplo:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

Resultado:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
Nota

parece que em muitos casos a opção gin__int_ops é necessária

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

Ainda não vi um caso em que funcionaria com o operador && e @> sem as opções gin__int_ops

Frank Heikens
fonte
19
Como o OP supõe, isso não indexa valores individuais da matriz, mas indexa a matriz inteira. Portanto, embora isso ajude a consulta em questão (consulte o plano de explicação), isso significa que você não pode criar restrições exclusivas (facilmente) nos valores individuais da matriz. Dito isto, se você estiver usando matrizes inteiras, poderá usar o módulo contrib "intarray" para indexar valores de matrizes individuais, que podem ser muito mais rápidos em muitos casos. (IIRC, há algum trabalho em andamento para valores de texto, mas os colaboradores provavelmente serão bem-vindos para ajudar a finalizá-lo).
Xzilla
15
Por favor, não use letras maiúsculas nos identificadores do PostgreSQL nos exemplos de código, apenas confunde as pessoas que não estão familiarizadas com as regras de cotação / dobra de maiúsculas e minúsculas, principalmente as novas no PostgreSQL.
intgr
6
Para repetir meu comentário aqui: pela minha experiência, esses índices oferecem pouca ou nenhuma aceleração, a menos que gin__int_ops sejam usados ​​para integer[]colunas. Levei anos de frustração e procurando outras soluções até descobrir essa classe operacional. É um milagreiro limítrofe.
IamIC # 28/17
1
@IamIC, isso significa que eu não deveria me incomodar em indexar uma matriz de strings? E eu só deveria indexar matrizes inteiras?
ryan2johnson9
93

@Tregoreg levantou uma pergunta no comentário para sua recompensa oferecida:

Não encontrei as respostas atuais funcionando. Usar o índice GIN na coluna do tipo matriz não aumenta o desempenho do operador ANY (). Realmente não há solução?

@ A resposta aceita por Frank diz para você usar operadores de matriz , o que ainda está correto para o Postgres 11. O manual:

... a distribuição padrão do PostgreSQL inclui uma classe de operador GIN para matrizes, que suporta consultas indexadas usando estes operadores:

<@
@>
=
&&

A lista completa de classes de operadores internas para índices GIN na distribuição padrão está aqui.

No Postgres, os índices são vinculados aos operadores (que são implementados para certos tipos), não apenas tipos de dados, funções ou qualquer outra coisa. Essa é uma herança do design original do Postgres de Berkeley e muito difícil de mudar agora. E geralmente está funcionando muito bem. Aqui está um tópico sobre pgsql-bugs com Tom Lane comentando sobre isso.

Algumas funções do PostGis (como ST_DWithin()) parecem violar esse princípio, mas não é assim. Essas funções são reescritas internamente para usar os respectivos operadores .

A expressão indexada deve estar à esquerda do operador. Para a maioria dos operadores ( incluindo todos os itens acima ), o planejador de consultas pode conseguir isso invertendo operandos se você colocar a expressão indexada à direita - desde que a COMMUTATORtenha sido definida. A ANYconstrução pode ser usada em combinação com vários operadores e não é um operador em si. Quando usado como constant = ANY (array_expression)apenas índices que suportam o =operador em elementos da matriz se qualificariam e precisaríamos de um comutador para = ANY(). Os índices GIN estão fora.

Atualmente, o Postgres não é inteligente o suficiente para derivar uma expressão indexável a GIN. Para iniciantes, nãoconstant = ANY (array_expression) é completamente equivalente a array_expression @> ARRAY[constant]. Os operadores de matriz retornam um erro se houver algum elemento NULL envolvido, enquanto a ANYconstrução pode lidar com NULL em ambos os lados. E há resultados diferentes para incompatibilidades de tipo de dados.

Respostas relacionadas:

Apartes

Ao trabalhar com integermatrizes ( int4, não int2ou int8) sem NULLvalores (como o seu exemplo indica), considere o módulo adicional intarray, que fornece operadores mais rápidos e especializados e suporte ao índice. Vejo:

Quanto à UNIQUErestrição em sua pergunta que ficou sem resposta: isso é implementado com um índice btree em todo o valor da matriz (como você suspeitava) e não ajuda na busca de elementos . Detalhes:

Erwin Brandstetter
fonte
1
Aaaaaaah, me sentindo bastante envergonhado agora, mas não me ocorreu que o postgres não usaria o índice, mesmo que teoricamente possível. Talvez seja também porque minha falta de conhecimento do postgres, como os índices, está vinculada aos operadores. Obrigado por responder a minha pergunta incorreta e compartilhar seu conhecimento!
31815 Tregoreg
6
@ Regoreg: Não fique muito envergonhado, não é realmente muito óbvio. Eu me lembro de ter me confundido com isso quando me deparei com isso. A pergunta e o esclarecimento adicionados devem ser bastante úteis para o público em geral.
Erwin Brandstetter 25/03
1
Pela minha experiência, esses índices oferecem pouca ou nenhuma aceleração, a menos que gin__int_ops sejam usados ​​para integer[]colunas. Levei anos de frustração e procurando outras soluções até descobrir essa classe operacional. É um milagreiro limítrofe.
IamIC # 28/17
2
@IamIC: eu adicionei ponteiros para a matriz. Parece digno de nota, como você apontou.
Erwin Brandstetter
Para ANY (array_expression) = constantexpressões, os índices GIN funcionam bem?
user10375
37

Agora é possível indexar os elementos individuais da matriz. Por exemplo:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

Isso funciona pelo menos no Postgres 9.2.1. Observe que você precisa criar um índice separado para cada índice de matriz. No meu exemplo, indexei apenas o primeiro elemento.

Ed4
fonte
28
Não se perca - essa abordagem é inútil para uma matriz de comprimento variável em que você deseja usar o operador ANY ().
Καrτhικ
24
Isso realmente não é muito útil. Se você tiver um número fixo de elementos da matriz, prefere usar colunas individuais para cada elemento (e índices de btree simples) em vez de criar um índice de expressão mais caro para cada item da matriz. O armazenamento de colunas individuais também é muito mais barato sem sobrecarga da matriz.
Erwin Brandstetter 25/03