Quinze anos de praia e essa eu não sabia, pode ser que vocês já sabiam, mas se não souberem, agora saberão. Pois bem, estava eu fulminando sobre o tema LINQ, que vocês já devem ter ouvido falar. Eu queria constatar a performance do trosso. Peguei uma situação típica de tabela Pai e Filho, sendo que a Filho teria um relacionamento com uma terceira tabelinha Auxiliar. Algo assim:
Eu tinha um cenário onde essa tabela Filho tinha 32mil registros, achei um bom começo para simular. Queria que me fosse listado todos os registros de Pai que tenham algum Filho que possua ligação com Auxiliar sendo essa com uma determinada descrição. Agora, poderia ter em Filho mais de uma ocorrência para o mesmo Pai que satisfizesse a condição. Por isso não poderia ser um simples JOIN entre as três tabelas (a resolução preguiçosa). Em meu cenário haviam 6 registros em Filho que satisfaziam a condição. Porém alocados em apenas 3 Pais, ou seja, o meu retorno precisaria ser 3 rows, os 3 pais. Um join entre pai e filho duplicaria esse resultado. Eu resolveria isso com a seguinte query:
Daí, escrevi uma rotininha boba em LINQ (fiz o model dessas tabelas, o relacionamento entre elas e tudo o mais) e rodei. A execução foi satisfatória, mas eu queria saber se ele tinha resolvido a tralha com um JOIN e um DISTINCT no resultado ou se tinha usado a subquery. Abri o SQL Profile e monitorei, para a minha alegria, ele resolve assim:
Bem, agora eu precisava forçar os limites. Rodei na base insanas vezes algo assim: “INSERT INTO Filho SELECT FROM Filho”. Até que isso deixou a tabela com 5 milhões de registros. Um pouco mais de trabalho pro engine, pensei eu. Rodo as querys, tanto a do LINQ quanto a minha e o resultado vem em 19 segundos. Um pouco desapontado, mas eu percebia que ali a culpa não era do LINQ, ambas estavam levando os 19 segundos. E foi aqui que eu aprendi algo novo essa semana. Rodei um ExecutionPlan na query para ver onde estaria o maior GAP. Afinal, estava tudo com as devidas PKs e FKs e eu já tinha colocado um índice na coluna Auxiliar.Descricao. Percebo que o GAP está na relação de Auxiliar e Filho. O que não é estranho, eu enfiei 5 milhões de registros ali. Mas tem uma FK poxa. Por desencargo de consciência, resolvi criar na Filho.idAuxiliar um índice simples. E a execução das querys caiu para… INSTANTÂNEA.
E foi isso que aprendi de novo. Toda minha vida eu sempre achei que, ao se amarrar duas tabelas com uma FK a relação entre elas será beneficiada por índices. Afinal, de um lado você tem uma Primary Key e do outro uma Foreign Key. O termo Key ae, sempre me fez entender que a coluna estaria indexada. Mas não, do lado da FK, é preciso criar um índice mesmo (além da FK, claro). Quando tiver um pouco mais de tempo, vou testar isso em outros engines (pelo menos no MySQL) para saber se isso é um comportamento natural, ou se é uma característica do MS SQL Server.



