ID do usuário | Primeiro | Meio | Durar | Tipo | Criado em |
---|---|---|---|---|---|
123 | John | Henrique | Corça | Mago | 28/03/2025 |
Digamos que eu tenha a seguinte tabela acima. Gostaria de criar um índice para ajudar a acelerar minhas consultas.
Todas as consultas devem ser do tipo:
Select *
from users
where Type = 'SomeType'
and First = 'SomeName1'
Order by CreatedAt DESC;
Select *
from users
where Type = 'SomeType'
and Middle = 'SomeName2'
Order by CreatedAt DESC;
Select *
from users
where Type = 'SomeType'
and Last = 'SomeName3'
Order by CreatedAt DESC;
Como eu indexaria as colunas para tornar as consultas eficientes? Seria CreatedAt
a primeira da coluna indexada?
Estou pensando
CREATE INDEX idx_users on users(CreatedAt, Type, First, Middle, Last)
CreatedAt
e type
sempre seria usado, enquanto primeiro, meio e último variam.
O índice que você propôs pode não ser útil:
O fato de você estar selecionando e ordenando por
CreatedAt
não significa que ele seja necessário como uma chave de índice. No meu teste em 400 mil amostras , nenhuma das três consultas que você mostrou acabou usando-o. Todas acabaram executando uma varredura sequencial que levou acima de150ms
.Se você simplesmente remover
createdat
do índice, eles ficarão felizes em usá-lo para uma varredura de índice de bitmap abaixo5ms
. Você pode adicionar mais ajustes dependendo de quanta latência de entrada você está pronto para tolerar e quanto espaço você está disposto a gastar para acomodar os índices.Aqui está uma comparação do espaço de armazenamento que eles ocupam em 200 mil linhas, com tempos de consulta em ms em média em 45 chamadas, mais o tamanho do índice e quanto mais espaço você precisa para ele, em % em comparação com o tamanho da tabela base:
demonstração em db<>fiddle
(Type,First,Middle,Last) include(createdat,userid);
(Type,First,CreatedAt) INCLUDE(Middle,Last,UserID);
(Type,Middle,CreatedAt) INCLUDE(First,Last,UserID);
(Type,Last,CreatedAt) INCLUDE (First,Middle,UserID);
charlieface
(Type,First,Middle,Last);
(Type,first);
(Type,middle);
(Type,last);
(first,type,createdat desc);
(middle,type,createdat desc);
(last,type,createdat desc);
Thorsten Kettner
(Type) include(First,Middle,Last, UserID,CreatedAt);
(Type);
(CreatedAt,Type,First,Middle,Last);
Indexação é sobre seletividade. Se houvesse um índice, e uma consulta selecionasse 50% das linhas na tabela, por exemplo, então seria uma má ideia para o DBMS usar o índice, pois isso daria muito trabalho comparado a apenas ler todas as linhas da tabela sequencialmente.
Há duas colunas em cada cláusula where. Qual das duas é mais seletiva? Vamos dar uma olhada na primeira consulta: eu esperaria que houvesse mais nomes distintos do que tipos, então o primeiro nome seria a primeira coluna no índice e o tipo seria apenas a segunda. Então, um índice para a primeira consulta ficaria assim:
Como você está usando apenas comparação de igualdade (Type = ... , First = ...), você atingirá uma posição no índice onde encontrará todas as correspondências. Isso nos permite adicionar a coluna CreatedAt ao índice, de modo que todas essas correspondências já estariam classificadas:
Para as outras duas consultas você teria:
Então, você precisa de três índices diferentes para as três consultas.
Se você quisesse fornecer apenas um único índice para todas as consultas, indexaria o tipo, porque esta é a coluna na cláusula where que todas as três consultas têm em comum. Você poderia adicionar as outras colunas, para fornecê-las antes que a tabela fosse lida, mas duvido que o DBMS usaria o índice, de qualquer forma, porque não presumo que o tipo seja seletivo o suficiente. Uma consulta pode se beneficiar, ou seja, aquela em que a segunda coluna indexada corresponderia à coluna na cláusula where, mas é isso. Portanto, fornecer apenas um índice não é uma opção promissora. Você deve fornecer três índices, um por consulta.
Esse índice não é útil, assim
CreatedAt
como a primeira coluna, o que significa que todas as outras colunas não podem ser buscadas.Além disso:
select *
. Você precisaria de todas as outras colunas em uminclude
.Middle
eLast
não estão ajudando você por estarem na chave do índice, pois estão depois deFirst
. Então esse índice (mesmo que estivesse cobrindo) só ajudará na primeira consulta.Então você precisa de três índices separados.
Você pode ver no violino que isso lhe dá uma varredura somente de índice em todas as três consultas.
db<>violino