← Voltar ao blog

Índices no Firebird: como escolher a coluna certa (e quando o índice atrapalha)

Índice errado deixa a query mais lenta e o INSERT mais pesado. Veja seletividade, ordem de colunas compostas e como ler o plano no Firebird.


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.

Índice não é um botão de "deixar rápido". É uma estrutura ordenada que o banco mantém em paralelo à tabela, e que só ajuda quando a consulta consegue aproveitar essa ordem. Escolher a coluna errada, ou a ordem errada dentro de um índice composto, é o motivo mais comum de uma query continuar lenta mesmo depois de "indexada".

Este guia mostra como o Firebird usa índices, como escolher as colunas, como ler o plano de execução e quando o melhor é não criar índice nenhum.


Como o Firebird usa um índice

O índice padrão do Firebird é uma árvore B (B-tree). Ele guarda os valores da coluna em ordem, junto com um ponteiro para a linha correspondente. Isso permite três operações baratas:

  • busca por igualdade (WHERE ID = ?), descendo direto até a chave;
  • busca por faixa (BETWEEN, >, <), percorrendo um trecho contínuo;
  • ordenação (ORDER BY), quando a ordem do índice coincide com a pedida.

O otimizador escolhe entre percorrer a tabela inteira (plano NATURAL) ou usar um índice. A decisão depende de estatísticas: quantas linhas a tabela tem, quantos valores distintos a coluna possui e quanto do índice será lido.

Se a consulta vai retornar 40% da tabela, ler o índice e depois buscar cada linha na tabela pode custar mais do que uma varredura sequencial. É por isso que índice em coluna de baixa seletividade raramente ajuda.

O problema: índice criado, query continua lenta

Considere uma tabela de apontamentos de horas 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 localiza o período e depois testa IDPROFISSIONAL linha a linha dentro dessa faixa. Em um mês com muitos profissionais, ainda é muita leitura.

O índice composto resolve o problema:

CREATE INDEX IDX_TIMECARDS_PROF_DATA
  ON TIMECARDS (IDPROFISSIONAL, DATA_APONTAMENTO);

Agora o banco desce direto no profissional e percorre apenas o intervalo de datas daquele profissional.

A ordem das colunas em um índice composto

Esta é a parte que mais gente erra. Em um índice composto, o Firebird percorre as colunas da esquerda para a direita. A primeira coluna precisa ser útil para reduzir o conjunto; uma coluna de faixa no começo limita o uso das seguintes.

Regra prática:

  • Colunas comparadas por igualdade (=, IN) vêm primeiro.
  • A coluna de faixa (BETWEEN, >, <) vem depois.
  • Colunas usadas apenas em ORDER BY podem entrar no fim, se ajudarem a evitar ordenação.
Consulta Índice recomendado
WHERE IDPROFISSIONAL = ? AND DATA BETWEEN ? AND ? (IDPROFISSIONAL, DATA)
WHERE IDCLIENTE = ? AND STATUS = ? AND DATA > ? (IDCLIENTE, STATUS, DATA)
WHERE IDCLIENTE = ? ORDER BY DATA DESC (IDCLIENTE, DATA)
WHERE STATUS = ? (2 valores possíveis) provavelmente nenhum

Note a diferença entre (IDCLIENTE) e (IDCLIENTE, DATA): o segundo cobre buscas por cliente e por cliente + data. O primeiro pode ser redundante se o segundo existir.

Seletividade: o cálculo que muda a decisão

Seletividade é a proporção de linhas que uma coluna consegue distinguir. Quanto mais valores distintos, melhor.

Exemplo com 1.000.000 de linhas:

Coluna Valores distintos Linhas por valor Seletividade
ATIVO (S/N) 2 ~500.000 péssima
STATUS (5 valores) 5 ~200.000 ruim
IDCLIENTE 50.000 ~20 ótima
ID (chave) 1.000.000 1 perfeita

O Firebird mantém estatísticas de seletividade por índice. Se os dados mudaram muito desde a criação (carga inicial, importação, limpeza), vale recalcular:

SET STATISTICS INDEX IDX_TIMECARDS_PROF_DATA;

O recálculo não muda os dados; apenas atualiza a informação que o otimizador usa para escolher o plano.

Como ler o plano de execução

Antes de criar mais um índice, descubra o que o banco está fazendo hoje. No isql:

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;

A saída mostra algo como:

PLAN (TIMECARDS INDEX (IDX_TIMECARDS_PROF_DATA))

Se aparecer PLAN (TIMECARDS NATURAL), o banco está varrendo a tabela inteira. Para ver o plano sem executar a consulta:

SET PLANONLY ON;
SELECT ...;
SET PLANONLY OFF;

Três leituras comuns:

  • INDEX (NOME_DO_INDICE) — o índice foi usado. Confirme se é o índice certo.
  • NATURAL — varredura completa. Pode ser correto (tabela pequena ou retorno grande) ou sintoma de índice ausente/inutilizável.
  • ORDER — houve ordenação em memória. Um índice adequado pode eliminar essa etapa.

Armadilhas que anulam o índice

  1. Função na coluna do filtro — WHERE UPPER(NOME) = ? não usa o índice de NOME, porque o valor indexado é o original, não o maiúsculo. Solução: índice calculado ou normalizar o dado na gravação.

    CREATE INDEX IDX_CLIENTES_NOME_UP
      ON CLIENTES COMPUTED BY (UPPER(NOME));
    
  2. Comparar tipos diferentes — string contra número força conversão e pode descartar o índice. Garanta que o bind envie o tipo correto (INTEGER como inteiro, VARCHAR como string).

  3. LIKE '%texto' — curinga no início impede a busca por faixa. O índice só ajuda em LIKE 'texto%'.

  4. OR entre colunas diferentes — muitas vezes vira varredura completa. UNION ALL de duas consultas indexadas pode ser mais rápido.

  5. Operação aritmética na coluna — WHERE VALOR * 2 > 100 não usa o índice de VALOR. Reescreva como WHERE VALOR > 50.

  6. NOT e <> — em colunas de baixa seletividade, o banco prefere varrer a tabela. Índice raramente resolve.

  7. Índice em tabela pequena — para dezenas ou centenas de linhas, a varredura natural já é barata e o índice só adiciona custo de manutenção.

Quando o índice atrapalha

Toda escrita paga o preço de todos os índices da tabela. Em uma tabela que recebe milhares de INSERTs por minuto, cinco índices significam cinco estruturas para atualizar a cada linha.

Sinais de que um índice está sobrando:

  • a consulta que ele deveria acelerar continua lenta;
  • existe outro índice com as mesmas colunas iniciais;
  • a coluna tem poucos valores distintos e aparece em quase todas as linhas;
  • o ganho na leitura é menor que a perda na escrita.

Para testar o impacto sem apagar o índice:

ALTER INDEX IDX_TIMECARDS_PROF_DATA INACTIVE;
-- medir leitura e escrita
ALTER INDEX IDX_TIMECARDS_PROF_DATA ACTIVE;

Em carga massiva (importação de milhares de linhas), desativar índices não essenciais durante o processo e reativar depois costuma valer mais que qualquer ajuste de query.

Passo a passo para investigar uma query lenta

  1. Capture a consulta real e os parâmetros. Query genérica no papel esconde o problema.
  2. Rode SET PLANONLY ON e veja se o índice esperado aparece.
  3. Liste os índices existentes na tabela e confira a ordem das colunas.
  4. Compare a seletividade das colunas usadas no WHERE.
  5. Crie um índice candidato e meça com o mesmo volume de dados do cliente.
  6. Verifique o INSERT/UPDATE da mesma tabela antes e depois.
  7. Mantenha só o que ganhou. Índice sem uso é imposto sobre a escrita.

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.
  • Evite função, aritmética e curinga no início da coluna filtrada.
  • Meça também o custo de escrita, não só o tempo do SELECT.
  • Remova índices redundantes.

Conclusão

Índice é uma ferramenta de precisão: resolve quando a coluna e a ordem certas encontram a consulta certa, e atrapalha quando vira reflexo automático. O caminho seguro é sempre o mesmo: medir com o plano, criar um candidato, comparar leitura e escrita e manter apenas o que comprovadamente ajuda.

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.

Leia também: