Arrays no PostgreSQL permitem guardar vários valores numa única célula da tabela. Isso é super prático quando tu precisa agrupar dados relacionados, tipo uma lista de tags pra um artigo ou as categorias de um produto. Só que, quando a gente começa a buscar, filtrar ou cruzar arrays, a performance pode cair feio. É aí que a indexação de arrays salva a pátria. Os índices aceleram operações como:
- verificar se um array contém um elemento específico,
- buscar arrays que contenham certos elementos,
- verificar se arrays têm interseção.
Operadores pra trabalhar com arrays
Antes de cair de cabeça na criação de índices, bora entender os operadores principais pra mexer com arrays:
@> (contains) — checa se o array contém todos os elementos de outro array.
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
Aqui a gente procura cursos que tenham a tag "SQL".
<@ (is contained by) — checa se um array está contido em outro.
SELECT *
FROM courses
WHERE ARRAY['PostgreSQL', 'SQL'] <@ tags;
Aqui a gente procura cursos cujas tags incluem todos os elementos do array ARRAY['PostgreSQL', 'SQL'].
&& (overlap) — checa se existe interseção entre arrays.
SELECT *
FROM courses
WHERE tags && ARRAY['NoSQL', 'Big Data'];
Essa query acha cursos que têm pelo menos uma das tags "NoSQL" ou "Big Data".
Como a indexação ajuda?
Imagina que tu tem uma tabela courses com milhões de registros, e tu faz uma query usando um desses operadores aí de cima. Sem índice, o PostgreSQL vai ter que checar cada linha uma por uma — um processo que pode demorar uma eternidade (especialmente se tu tem a paciência de um dev esperando a compilação terminar).
Com índices, tu foge desse sofrimento. O PostgreSQL oferece dois tipos de índices que são massa pra arrays:
GIN(Generalized Inverted Index) — melhor escolha pra arrays.BTREE— usado pra comparar arrays inteiros.
Exemplo: Criando índice pra arrays
Bora criar uma tabelinha com arrays pra testar tudo na prática.
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
tags TEXT[] NOT NULL
);
Vamos adicionar uns registros:
INSERT INTO courses (name, tags)
VALUES
('Fundamentos de SQL', ARRAY['SQL', 'PostgreSQL', 'Bancos de dados']),
('Trabalhando com Big Data', ARRAY['Hadoop', 'Big Data', 'NoSQL']),
('Desenvolvimento em Python', ARRAY['Python', 'Web', 'Dados']),
('Curso de PostgreSQL', ARRAY['PostgreSQL', 'Advanced', 'SQL']);
Olha como fica a tabela:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 2 | Trabalhando com Big Data | {Hadoop, Big Data, NoSQL} |
| 3 | Desenvolvimento em Python | {Python, Web, Dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
Sem índice: busca lenta
Agora imagina que a gente quer achar todos os cursos que têm a tag SQL.
EXPLAIN ANALYZE
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
Essa query até roda, mas se tiver muita linha, vai ficar super lenta. O PostgreSQL vai fazer o famoso scan sequencial (Sequential Scan), ou seja, vai olhar linha por linha.
Exemplo de resultado:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
Criando índice GIN
Pra acelerar a busca, bora criar um índice do tipo GIN:
CREATE INDEX idx_courses_tags
ON courses USING GIN (tags);
Agora vamos rodar a mesma query:
EXPLAIN ANALYZE
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
Agora o PostgreSQL vai usar o índice GIN que a gente criou, e o tempo de execução cai muito.
Se antes era scan sequencial (Seq Scan), agora no plano de execução tu vai ver Bitmap Index Scan:
| Step | Rows | Cost | Info |
|---|---|---|---|
| Bitmap Index Scan | N | baixo | pelo índice idx_courses_tags |
| Bitmap Heap Scan | N | baixo | linhas selecionadas da tabela |
Os valores exatos de Rows e Cost dependem do tamanho dos dados, mas o importante é que agora o índice aparece no plano.
Como os operadores funcionam com índices?
Exemplo 1: Operador @>
Query:
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
O índice GIN é perfeito pra esse operador. O Postgres acha rapidinho quais linhas têm o elemento e retorna o resultado.
Resultado:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
@> se lê "contém" — essa query retorna todos os cursos onde o array tags tem o valor SQL.
Exemplo 2: Operador &&
Query:
SELECT *
FROM courses
WHERE tags && ARRAY['NoSQL', 'Big Data'];
Esse operador checa interseção de arrays: ele retorna linhas onde o array tags tem pelo menos um elemento do array passado.
O índice GIN faz a mágica de novo — a busca é rápida, mesmo com muitos dados.
Resultado:
| id | name | tags |
|---|---|---|
| 2 | Trabalhando com Big Data | {Hadoop, Big Data, NoSQL} |
se lê "tem interseção" — a condição é verdadeira se pelo menos uma tag bate.
Indexação e otimização
Quando for trabalhar com arrays, segue essas dicas:
- Usa índices
GINpra buscar dentro de arrays. Eles são muito mais rápidos que scan sequencial. - Só cria índice nos campos que tu realmente usa nas queries. Índice ocupa espaço e deixa inserts mais lentos, então não sai indexando tudo.
- Perfilha tuas queries com
EXPLAINeEXPLAIN ANALYZEpra ver se o índice tá sendo usado mesmo.
Exemplos: criando índices pra arrays
Bora ver como criar índices pra tipos específicos de operação com arrays e por que isso é útil na prática.
Índice pro operador @>
Imagina que a gente já tem essa tabela courses:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 2 | Trabalhando com Big Data | {Hadoop, Big Data, NoSQL} |
| 3 | Desenvolvimento em Python | {Python, Web, Dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
Pra acelerar queries com o operador @> (array contém elemento), cria um índice GIN:
CREATE INDEX idx_courses_tags_gin
ON courses USING GIN (tags);
Agora roda a query:
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
Resultado:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
Índice pros operadores @>, <@ e &&
A tabela é a mesma do exemplo anterior.
Como os operadores @>, <@ e && funcionam bem com índices GIN, tu pode criar um índice só que acelera queries com qualquer um desses operadores:
CREATE INDEX idx_tags
ON courses USING GIN (tags);
Exemplos de queries e resultados:
@>— checa se o array contém os elementos:
SELECT *
FROM courses
WHERE tags @> ARRAY['SQL'];
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
<@— checa se o array está contido em outro array:
SELECT *
FROM courses
WHERE tags <@ ARRAY['SQL', 'PostgreSQL', 'Advanced', 'Big Data', 'NoSQL', 'Python'];
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL, PostgreSQL, Bancos de dados} |
| 2 | Trabalhando com Big Data | {Hadoop, Big Data, NoSQL} |
| 3 | Desenvolvimento em Python | {Python, Web, Dados} |
| 4 | Curso de PostgreSQL | {PostgreSQL, Advanced, SQL} |
&&— checa interseção de arrays:
SELECT *
FROM courses
WHERE tags && ARRAY['NoSQL', 'Big Data'];
| id | name | tags |
|---|---|---|
| 2 | Trabalhando com Big Data | {Hadoop, Big Data, NoSQL} |
Bora tentar algo mais difícil
Vamos fazer uma query que retorna cursos onde as tags têm pelo menos uma interseção com a lista ['Python', 'SQL', 'NoSQL']:
SELECT *
FROM courses
WHERE tags && ARRAY['Python', 'SQL', 'NoSQL'];
Saída:
| id | name | tags |
|---|---|---|
| 1 | Fundamentos de SQL | {SQL,PostgreSQL,Bancos de dados} |
| 2 | Trabalhando com Big Data | {Hadoop,Big Data,NoSQL} |
| 3 | Desenvolvimento em Python | {Python,Web,Dados} |
Com índice GIN essa query roda na hora, mesmo se a tabela tiver milhões de linhas.
Erros comuns ao trabalhar com arrays
Índice não é usado: se no output do EXPLAIN tu vê Seq Scan, confere se o índice foi criado e se o operador que tu tá usando realmente suporta indexação.
Uso raro do array: se o campo de array quase nunca aparece nas queries ou é atualizado com frequência, o índice pode só ocupar espaço e não ajudar em nada.
Índices demais: índices ocupam espaço em disco e deixam inserts mais lentos, então só cria os que tu realmente vai usar nas queries.
Agora tu tem todas as ferramentas pra trabalhar de boa com arrays no PostgreSQL — acelera tuas queries usando os operadores @>, <@, && e índices GIN. Testa aí nos teus dados sem medo!
GO TO FULL VERSION