Seletividade, Cardinalidade e Distribuição de Dados: Escolhendo Colunas para Indexar

Capítulo 6

Tempo estimado de leitura: 8 minutos

+ Exercício

Seletividade e cardinalidade: o que são e por que importam

Cardinalidade é a quantidade de valores distintos em uma coluna dentro de uma tabela (ou dentro de um recorte, como “no último mês”).

Seletividade é o quão “filtrante” é um predicado (condição) sobre uma coluna. Uma forma prática de pensar é: seletividade ≈ linhas_retornadas / linhas_totais. Quanto menor esse número, mais seletivo é o filtro (melhor para reduzir o conjunto de linhas).

Em termos de escolha de colunas para indexar, colunas com alta cardinalidade (muitos valores distintos) costumam permitir filtros mais seletivos, especialmente em condições de igualdade (=) ou intervalos bem estreitos.

Exemplo numérico (tabela com 1.000.000 linhas)

ColunaValores distintos (cardinalidade)Exemplo de filtroLinhas esperadasSeletividade
status4status = 'PAGO'250.00025%
cliente_id200.000cliente_id = 123~50,0005%
pais20pais = 'BR'100.00010%
data3.650 (10 anos)data = '2026-01-01'~2740,0274%

Repare como status tem baixa cardinalidade e tende a retornar muitas linhas. Um índice em status pode trazer pouco benefício em muitas situações, porque o banco ainda precisará visitar uma grande quantidade de linhas.

Por que valores repetidos tendem a trazer menos benefício

Quando uma coluna tem poucos valores possíveis (ex.: status com 4 opções), um filtro típico (status = 'X') costuma selecionar uma fatia grande da tabela. Mesmo com índice, o banco pode acabar fazendo muitas leituras para buscar todas as linhas correspondentes.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Mini-simulação

Imagine que a consulta precisa retornar 250.000 linhas (25% da tabela). Mesmo que o índice encontre rapidamente “onde estão” essas linhas, o custo de buscar e processar 250.000 registros pode ser próximo (ou pior) do que varrer a tabela, dependendo de:

  • tamanho da tabela e das páginas em disco
  • quantas colunas a consulta precisa (se precisa buscar muitas colunas fora do índice)
  • se os dados estão em cache
  • se a consulta ainda precisa ordenar/agrupar

Distribuição de dados: uniforme vs concentrada

Cardinalidade sozinha não conta toda a história. A distribuição dos valores muda completamente a seletividade real.

Distribuição uniforme

Em uma distribuição aproximadamente uniforme, cada valor aparece com frequência parecida.

statusLinhas%
PAGO250.00025%
PENDENTE250.00025%
CANCELADO250.00025%
ESTORNADO250.00025%

Nesse caso, qualquer filtro por status retorna muita coisa.

Distribuição concentrada (skew)

Agora considere que a maioria está em um único valor:

statusLinhas%
PAGO900.00090%
PENDENTE80.0008%
CANCELADO15.0001,5%
ESTORNADO5.0000,5%

Aqui, um índice em status pode ser muito útil para consultas como status = 'ESTORNADO' (0,5%), mas pode ser pouco útil para status = 'PAGO' (90%).

Ou seja: a pergunta não é apenas “quantos valores distintos existem?”, mas também “qual valor eu filtro com mais frequência e qual a fatia que ele representa?”.

Valores nulos: como afetam seletividade e escolha de índice

NULL costuma se comportar como um “valor especial” em termos de distribuição: pode ser raro ou muito comum. Isso afeta a seletividade.

Exemplo: coluna data_pagamento

Suponha data_pagamento com muitos NULL (pedidos ainda não pagos):

CondiçãoLinhas%Comentário
data_pagamento IS NULL600.00060%Pouco seletivo
data_pagamento IS NOT NULL400.00040%Ainda pouco seletivo
data_pagamento = '2026-01-01'1.0000,1%Bem seletivo

Se o padrão de consulta é “buscar pendências” com IS NULL, um índice nessa coluna pode não ajudar tanto (retorna muita linha). Se o padrão é “buscar pagamentos de um dia específico”, pode ajudar bastante.

Em alguns bancos, também é comum usar índices parciais/filtrados para lidar com NULL (ex.: indexar apenas linhas onde data_pagamento IS NULL), mas isso depende do SGBD e do caso de uso.

Como combinações de colunas aumentam seletividade

Quando uma coluna isolada não é seletiva o suficiente, combinar colunas pode reduzir drasticamente o número de linhas retornadas.

Exemplo: status vs (status + data)

Tabela com 1.000.000 linhas, distribuídas ao longo de 365 dias (1 ano). Suponha:

  • status tem 4 valores (uniforme): cada um ~25%
  • data tem 365 valores: cada dia ~0,27%

Compare:

FiltroEstimativa de linhasSeletividade
status = 'PENDENTE'250.00025%
data = '2026-01-01'~2.7400,274%
status = 'PENDENTE' AND data = '2026-01-01'~6850,0685%

Mesmo que status sozinho seja fraco, a combinação com data pode ficar excelente para consultas que sempre filtram pelos dois.

Cuidado: correlação entre colunas

As contas acima assumem independência (distribuição “misturada”). Se houver correlação, a seletividade real muda. Exemplo: se “PENDENTE” ocorre quase todo em datas recentes, então status='PENDENTE' AND data='2026-01-01' pode retornar bem mais do que o estimado.

Na prática, você valida isso olhando contagens reais (amostras) e o plano de execução.

Passo a passo prático: como decidir quais colunas indexar

Passo 1 — Liste as consultas típicas (padrões de filtro)

Exemplos de consultas comuns em uma tabela pedidos:

Q1: listar pedidos de um cliente (recentes ou todos)
Q2: listar pedidos por status (ex.: pendentes)
Q3: relatórios por período (data entre X e Y)
Q4: pedidos por país e período

Passo 2 — Estime seletividade com números (mesmo que aproximados)

Use contagens aproximadas. Exemplo hipotético:

ColunaCardinalidadeDistribuiçãoObservação
status4concentrada (90% PAGO)bom para valores raros
data3.650quase uniforme por diabom para intervalos curtos
cliente_id200.000concentrada (clientes grandes)ótimo para igualdade; varia por cliente
pais20concentrada (BR 60%)sozinho costuma ser fraco

Passo 3 — Priorize colunas que reduzem muito o conjunto de linhas nas consultas mais frequentes

  • Filtros por cliente_id = ? tendem a ser altamente seletivos.
  • Filtros por data podem ser muito seletivos se o intervalo for curto (ex.: 1 dia, 7 dias), e pouco seletivos se for longo (ex.: 2 anos).
  • status e pais sozinhos muitas vezes retornam “fatias grandes” e podem não compensar.

Passo 4 — Considere combinações quando a consulta filtra por mais de uma coluna

Se a consulta típica filtra por cliente_id e também restringe por data, a combinação pode reduzir ainda mais leituras. Se filtra por pais e data para relatórios, a combinação pode ser mais interessante do que pais sozinho.

Passo 5 — Leve em conta o tamanho da tabela e o custo de manutenção

Em tabelas pequenas, a diferença pode ser imperceptível. Em tabelas grandes, qualquer filtro que evite ler uma grande fração da tabela tende a valer mais. Ao mesmo tempo, cada índice extra aumenta custo de escrita (inserções/atualizações) e ocupa espaço.

Tabelas hipotéticas para exercícios (status, data, cliente_id, país)

Cenário A — 10 milhões de pedidos (distribuição mista)

ColunaResumo
status4 valores; PAGO 85%, PENDENTE 10%, CANCELADO 4%, ESTORNADO 1%
data2 anos (730 dias); últimos 30 dias concentram 35% dos pedidos
cliente_id500.000 clientes; top 1% gera 25% dos pedidos
pais30 países; BR 55%, US 15%, demais diluídos

Consultas típicas

Q1: SELECT * FROM pedidos WHERE cliente_id = ? ORDER BY data DESC LIMIT 50;
Q2: SELECT * FROM pedidos WHERE status = 'PENDENTE' AND data >= ? AND data < ?;
Q3: SELECT count(*) FROM pedidos WHERE pais = 'BR' AND data >= ? AND data < ?;
Q4: SELECT * FROM pedidos WHERE status = 'PAGO' AND pais = 'BR';

Exercícios guiados (com perguntas e respostas)

Exercício 1 — Qual coluna indexar primeiro?

Pergunta: No Cenário A, qual coluna tende a ser a melhor candidata para acelerar Q1?

Resposta guiada: cliente_id, porque o filtro por igualdade geralmente reduz muito o conjunto de linhas. Além disso, Q1 ordena por data e limita 50 resultados; isso sugere que uma estratégia baseada em cliente_id é central para essa consulta.

Exercício 2 — status sozinho vale a pena?

Pergunta: Um índice apenas em status ajudaria Q2?

Resposta guiada: Depende do valor filtrado e da janela de datas. status='PENDENTE' é 10% (ainda 1 milhão de linhas em 10 milhões). Sozinho pode ser pesado. Mas Q2 também restringe por data; a seletividade real vem da combinação: “pendentes em um intervalo curto”. Então, pensar em indexar levando em conta ambos os filtros costuma fazer mais sentido do que status isolado.

Exercício 3 — país é fraco, mas pode ser útil?

Pergunta: Para Q3, indexar pais sozinho é uma boa?

Resposta guiada: Geralmente pais sozinho é pouco seletivo (BR 55%). Mas Q3 sempre combina pais com data. Se o intervalo de data for curto (ex.: 7 dias), data tende a ser o filtro mais seletivo; se for longo (ex.: 1 ano), a combinação com pais pode ajudar a reduzir leituras. A decisão depende do intervalo típico usado no relatório.

Exercício 4 — Quando um valor muito comum “estraga” o índice

Pergunta: Em Q4, status='PAGO' (85%) e pais='BR' (55%). Isso é seletivo?

Resposta guiada: Não muito. Mesmo combinando, a interseção pode continuar grande (por exemplo, se forem relativamente independentes: 0,85 × 0,55 ≈ 46,75% da tabela). Um índice para essa consulta pode não compensar, porque ainda retornaria quase metade das linhas. Aqui, a pergunta correta é: essa consulta realmente precisa retornar tantas linhas? Se sim, o gargalo pode não ser “achar” as linhas, mas processar/transferir o volume.

Checklist rápido: perguntas que você deve responder antes de indexar

  • Qual é a seletividade real do filtro mais comum nessa coluna (não apenas a cardinalidade)?
  • O valor filtrado é raro ou muito comum?
  • A distribuição é uniforme ou concentrada (skew)?
  • Há muitos NULL? As consultas filtram por IS NULL ou por valores específicos?
  • As consultas filtram por uma coluna ou por combinações?
  • Qual é o tamanho da tabela e o volume de escrita (custo de manter índices)?

Prática orientada: estime seletividade com uma tabela de contagens

Use uma tabela de contagens (real ou hipotética) para decidir. Exemplo (Cenário A):

FiltroLinhas estimadasSeletividadeComentário
cliente_id = 123200,0002%excelente
status = 'ESTORNADO'100.0001%boa para esse valor
status = 'PAGO'8.500.00085%ruim para esse valor
pais = 'BR'5.500.00055%fraca sozinha
data em 7 dias (últimos)~1.225.00012,25%depende do período
status='PENDENTE' e 7 dias~122.5001,225%combinação melhora

Pergunta final (para você responder): quais dessas condições aparecem com mais frequência no seu sistema? A coluna (ou combinação) que mais aparece nas consultas críticas e reduz mais linhas tende a ser a melhor candidata.

Agora responda o exercício sobre o conteúdo:

Em uma tabela grande, qual situação tende a tornar um índice mais útil para acelerar uma consulta?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Índices tendem a ajudar mais quando o predicado filtra bem (baixa seletividade: poucas linhas retornadas) ou quando a combinação de colunas diminui muito o volume de linhas a buscar. Se o filtro retorna uma grande fatia da tabela, o ganho pode ser pequeno.

Próximo capitúlo

Custo de Escrita e Manutenção de Índices: Impacto em INSERT, UPDATE e DELETE

Arrow Right Icon
Capa do Ebook gratuito Índices e Performance de Banco de Dados para Iniciantes: Como Acelerar Consultas sem Mistério
43%

Índices e Performance de Banco de Dados para Iniciantes: Como Acelerar Consultas sem Mistério

Novo curso

14 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.