Quando uma consulta começa a demorar, o primeiro reflexo é criar índice em tudo. Em Firebird, isso costuma piorar o cenário: cada índice extra aumenta o custo de INSERT, UPDATE e DELETE, e o otimizador pode escolher um plano ruim.
Este artigo mostra como decidir onde criar índice, em qual ordem, e como confirmar que o Firebird está realmente usando o que você criou.
O problema: índice criado, query continua lenta
Considere uma tabela de apontamentos com milhões de linhas:
SELECT IDPROFISSIONAL, DATA_APONTAMENTO, HORAS
FROM TIMECARDS
WHERE DATA_APONTAMENTO BETWEEN ? AND ?
AND IDPROFISSIONAL = ?;
Se existir apenas um índice em DATA_APONTAMENTO, o Firebird filtra o período e depois testa IDPROFISSIONAL linha a linha. Em um mês com muitos profissionais, ainda é muita leitura.
O índice composto resolve:
CREATE INDEX IDX_TIMECARDS_PROF_DATA
ON TIMECARDS (IDPROFISSIONAL, DATA_APONTAMENTO);
A ordem das colunas importa
Em índice composto, coloque primeiro as colunas comparadas por igualdade (=) e depois a coluna usada em faixa (BETWEEN, >, <). O Firebird percorre o índice da esquerda para a direita; uma coluna de faixa no começo limita a busca nas seguintes.
Regra prática:
WHERE IDPROFISSIONAL = ? AND DATA BETWEEN ? AND ?→ índice(IDPROFISSIONAL, DATA).WHERE IDCLIENTE = ? AND STATUS = ? AND DATA > ?→ índice(IDCLIENTE, STATUS, DATA).- Índice só em
STATUSraramente ajuda: poucos valores distintos significam baixa seletividade.
Seletividade: o que realmente separa as linhas
Seletividade é a proporção de linhas que uma coluna consegue distinguir. IDCLIENTE em uma tabela de vendas é ótimo. ATIVO (S/N) é péssimo, porque metade da tabela casa com cada valor.
O Firebird mantém estatísticas de seletividade por índice. Se os dados mudaram muito desde a criação, vale recalcular:
SET STATISTICS INDEX IDX_TIMECARDS_PROF_DATA;
Antes de sair criando índices, confirme qual plano o otimizador está usando:
SET PLAN ON;
SELECT IDPROFISSIONAL, DATA_APONTAMENTO, HORAS
FROM TIMECARDS
WHERE IDPROFISSIONAL = 42
AND DATA_APONTAMENTO BETWEEN '2026-10-01' AND '2026-10-31';
SET PLAN OFF;
Para ver o plano sem executar a consulta:
SET PLANONLY ON;
SELECT ...;
SET PLANONLY OFF;
Se o plano não cita seu índice, desconfie de tipo de dado, ordem das colunas ou estatística desatualizada.
Armadilhas que anulam o índice
- Função na coluna do filtro —
WHERE UPPER(NOME) = ?não usa o índice deNOME. Crie um índice calculado ou normalize o dado na gravação. - Comparar tipos diferentes — string contra número força conversão e pode descartar o índice. Garanta que o bind envie o tipo certo.
LIKE '%texto'— curinga no início impede a busca por faixa no índice.ORentre colunas diferentes — muitas vezes vira varredura completa.UNION ALLde duas consultas indexadas pode ser mais rápido.- Índice em tabela pequena — para dezenas ou centenas de linhas, a varredura natural já é barata.
Quando o índice atrapalha
Tabelas com muita escrita pagam caro por cada índice adicional. Em uma tabela que recebe milhares de INSERTs por minuto, cinco índices significam cinco estruturas para atualizar a cada linha. Meça o ganho na leitura antes de manter o índice.
Também desconfie de índices redundantes: (IDCLIENTE) e (IDCLIENTE, DATA) não são a mesma coisa, mas o primeiro pode ser dispensável se o segundo já cobre as consultas por cliente.
Para testar o impacto sem apagar o índice, desative-o temporariamente:
ALTER INDEX IDX_TIMECARDS_PROF_DATA INACTIVE;
-- medir leitura e escrita
ALTER INDEX IDX_TIMECARDS_PROF_DATA ACTIVE;
Checklist rápido
- Filtre por igualdade primeiro, faixa depois.
- Prefira colunas com muitos valores distintos.
- Leia o plano antes e depois da mudança.
- Recalcule estatísticas após grandes cargas.
- Meça também o custo de escrita, não só o tempo do SELECT.
Se você já sabe o que quer consultar mas não quer escrever o SQL na mão, o Sqlify gera a query Firebird a partir da descrição em português, com explicação de cada cláusula. Descreva algo como "apontamentos do profissional X no mês" e receba a consulta pronta para revisar e otimizar.