Muitas consultas acessam apenas uma pequena fração das tuplas de uma tabela. Percorrer todas as linhas para encontrar as que satisfazem um predicado — varredura sequencial (sequential scan) — é ineficiente quando a tabela é grande. Um índice resolve esse problema.
O Que é um Índice
Um índice sobre um atributo de uma relação é uma estrutura de dados que permite ao SGBD encontrar as tuplas que possuem um valor específico para aquele atributo sem precisar percorrer todas as tuplas da relação.
A sintaxe para criar um índice é:
CREATEINDEX<nome>ON<relação> (<atributo>);
O índice mais comum em SGBDs relacionais é a B-tree (árvore-B), que suporta eficientemente buscas por igualdade, comparações e ordenações. O PostgreSQL também oferece índices HASH, GIN e GiST para casos específicos.
Criando Índices
-- Índice simples sobre id_curso na tabela aluno-- Acelera consultas como: WHERE id_curso = 311CREATEINDEX idx_aluno_curso ON aluno (id_curso);
-- Índice composto: acelera consultas que filtram por ano E semestreCREATEINDEX idx_ministra_ano_sem ON ministra (ano, semestre);
-- Índice único: garante unicidade além de acelerar buscasCREATEUNIQUEINDEX idx_prof_nome_escola ON professor (nome, id_escola);
Verificando os índices criados no banco:
SELECT tablename AS tabela, indexname AS indice, indexdef AS definicaoFROM pg_indexesWHERE schemaname ='public'ORDERBY tablename, indexname;
Displaying records 1 - 10
tabela
indice
definicao
aluno
aluno_pkey
CREATE UNIQUE INDEX aluno_pkey ON public.aluno USING btree (id_aluno)
centro
centro_pkey
CREATE UNIQUE INDEX centro_pkey ON public.centro USING btree (id_centro)
centro
centro_sigla_key
CREATE UNIQUE INDEX centro_sigla_key ON public.centro USING btree (sigla)
curso
curso_pkey
CREATE UNIQUE INDEX curso_pkey ON public.curso USING btree (id_curso)
curso
curso_sigla_curso_key
CREATE UNIQUE INDEX curso_sigla_curso_key ON public.curso USING btree (sigla_curso)
disciplina
disciplina_pkey
CREATE UNIQUE INDEX disciplina_pkey ON public.disciplina USING btree (id_disciplina)
escola
escola_pkey
CREATE UNIQUE INDEX escola_pkey ON public.escola USING btree (id_escola)
escola
escola_sigla_key
CREATE UNIQUE INDEX escola_sigla_key ON public.escola USING btree (sigla)
evento_academico
evento_academico_nome_data_inicio_key
CREATE UNIQUE INDEX evento_academico_nome_data_inicio_key ON public.evento_academico USING btree (nome, data_inicio)
evento_academico
evento_academico_pkey
CREATE UNIQUE INDEX evento_academico_pkey ON public.evento_academico USING btree (id_evento)
Por Que Índices Aceleram Consultas
Sem índice, para executar:
SELECT*FROM aluno WHERE id_curso =311;
o SGBD lê cada linha da tabela aluno e verifica o predicado — O(n) operações.
Com um índice B-tree sobre id_curso, o SGBD percorre a árvore em O(log n) para encontrar as entradas com id_curso = 311, depois acessa diretamente as páginas de disco onde essas linhas estão armazenadas.
-- EXPLAIN mostra o plano de execução: com índice vs. sem índice-- (Com tabelas pequenas como a da UnDF, o planner pode preferir seq scan mesmo com índice)EXPLAINSELECT*FROM alunoWHERE id_curso =311;
2 records
QUERY PLAN
Seq Scan on aluno (cost=0.00..16.38 rows=3 width=130)
Filter: (id_curso = 311)
Tradeoffs dos Índices
Aviso
Índices não são gratuitos. Cada índice:
Ocupa espaço em disco — pode ser significativo em tabelas grandes.
Retarda inserções, atualizações e exclusões — o SGBD precisa manter o índice sincronizado com os dados.
Não é sempre utilizado — o otimizador de consultas decide quando usar um índice com base em estatísticas da tabela.
Boas candidatas a índice: colunas usadas frequentemente em WHERE, JOIN ON, ORDER BY e que têm alta cardinalidade (muitos valores distintos).
Más candidatas: colunas booleanas ou com poucos valores distintos (como aprovado IN (0, 1)) — o índice raramente compensa o custo.
Índice Parcial
O PostgreSQL permite criar índices parciais — que indexam apenas as linhas que satisfazem um predicado. São menores e mais eficientes que índices completos quando o caso de uso foco em um subconjunto:
-- Índice apenas para matrículas reprovadas (aprovado = 0)-- Útil se consultas de reprovados são frequentesCREATEINDEX idx_reprovados ON matricula_disciplina (id_aluno)WHERE aprovado =0;
Para Praticar
-- Criar índice na coluna nome de aluno (busca por nome é comum)CREATEINDEXIFNOTEXISTS idx_aluno_nome ON aluno (nome);
-- Listar índices criados explicitamente (excluindo os de PK e FK automáticos)SELECT indexname, tablename, indexdefFROM pg_indexesWHERE schemaname ='public'AND indexname NOTLIKE'pg_%'AND indexname NOTLIKE'%_pkey'ORDERBY tablename, indexname;
7 records
indexname
tablename
indexdef
idx_aluno_nome
aluno
CREATE INDEX idx_aluno_nome ON public.aluno USING btree (nome)
centro_sigla_key
centro
CREATE UNIQUE INDEX centro_sigla_key ON public.centro USING btree (sigla)
curso_sigla_curso_key
curso
CREATE UNIQUE INDEX curso_sigla_curso_key ON public.curso USING btree (sigla_curso)
escola_sigla_key
escola
CREATE UNIQUE INDEX escola_sigla_key ON public.escola USING btree (sigla)
evento_academico_nome_data_inicio_key
evento_academico
CREATE UNIQUE INDEX evento_academico_nome_data_inicio_key ON public.evento_academico USING btree (nome, data_inicio)
reserva_sala_id_sala_id_prof_data_reserva_key
reserva_sala
CREATE UNIQUE INDEX reserva_sala_id_sala_id_prof_data_reserva_key ON public.reserva_sala USING btree (id_sala, id_prof, data_reserva)
sala_codigo_key
sala
CREATE UNIQUE INDEX sala_codigo_key ON public.sala USING btree (codigo)
-- EXPLAIN ANALYZE: tempo real de execução com e sem uso de índice-- (resultado varia conforme o tamanho dos dados e as estatísticas do planner)EXPLAINANALYZESELECT a.nome, c.nome_cursoFROM aluno aJOIN curso c USING (id_curso)WHERE a.nome LIKE'A%';
-> Seq Scan on aluno a (cost=0.00..1.23 rows=1 width=122) (actual time=0.005..0.006 rows=1 loops=1)
Filter: ((nome)::text ‘A%’::text)
Rows Removed by Filter: 17
-> Index Scan using curso_pkey on curso c (cost=0.14..8.16 rows=1 width=222) (actual time=0.003..0.003 rows=1 loops=1)
Index Cond: (id_curso = a.id_curso)
Planning Time: 0.277 ms
Execution Time: 0.021 ms
Nota
Com o banco da UnDF (18 alunos, 7 professores), o planner PostgreSQL tende a usar sequential scan porque as tabelas são pequenas demais para justificar o overhead de acessar um índice. Em produção, com milhares ou milhões de linhas, índices fazem diferença substancial.