Percebo que, quando há eventos spill to tempdb (causando consultas lentas), muitas vezes as estimativas de linha estão longe de uma associação específica. Eu já vi eventos de derramamento ocorrerem com junções de mesclagem e hash, e eles geralmente aumentam o tempo de execução de 3x para 10x. Esta pergunta diz respeito a como melhorar as estimativas de linha, supondo que reduzirá as chances de eventos de derramamento.
Número real de linhas 40k.
Para esta consulta, o plano mostra uma estimativa de linha incorreta (11,3 linhas):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Para esta consulta, o plano mostra uma boa estimativa de linha (56k linhas):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
É possível adicionar estatísticas ou dicas para melhorar as estimativas de linha para o primeiro caso? Tentei adicionar estatísticas com valores de filtro específicos (propriedade = 2840), mas não foi possível obter a combinação correta ou talvez esteja sendo ignorada porque o ObjectId é desconhecido no momento da compilação e pode estar escolhendo uma média entre todos os ObjectIds.
Existe algum modo em que ele faça a consulta do probe primeiro e depois o use para determinar as estimativas de linha ou deve voar às cegas?
Essa propriedade em particular possui muitos valores (40k) em alguns objetos e zero na grande maioria. Eu ficaria feliz com uma dica em que o número máximo esperado de linhas para uma determinada junção poderia ser especificado. Esse é um problema geralmente assustador, porque alguns parâmetros podem ser determinados dinamicamente como parte da junção ou seriam melhor colocados em uma exibição (sem suporte para variáveis).
Existem parâmetros que podem ser ajustados para minimizar as chances de vazamentos para tempdb (por exemplo, min de memória por consulta)? O plano robusto não teve efeito na estimativa.
Edite 2013.11.06 : resposta a comentários e informações adicionais:
Aqui estão as imagens do plano de consulta. Os avisos são sobre o predicado de cardinalidade / busca com o convert ():
Pelo comentário de @Aaron Bertrand, tentei substituir o convert () como um teste:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Como um ponto de interesse estranho, mas bem-sucedido, também permitiu um curto-circuito na pesquisa:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Ambos listam uma pesquisa de chave adequada, mas apenas os primeiros listam uma "Saída" do ObjectId. Eu acho que isso indica que o segundo é realmente um curto-circuito?
Alguém pode verificar se as análises de linha única são executadas para ajudar nas estimativas de linha? Parece errado limitar a otimização a apenas estimativas de histograma quando uma pesquisa de PK de linha única pode melhorar bastante a precisão da pesquisa no histograma (especialmente se houver histórico ou potencial de derramamento). Quando existem 10 dessas sub-junções em uma consulta real, o ideal seria que elas acontecessem em paralelo.
Uma observação: como o sql_variant armazena seu tipo base (SQL_VARIANT_PROPERTY = BaseType) dentro do próprio campo, eu esperaria que um convert () fosse praticamente gratuito, desde que seja conversível "diretamente" (por exemplo, não string para decimal, mas sim int) int ou talvez int para bigint). Como isso não é conhecido no momento da compilação, mas pode ser conhecido pelo usuário, talvez uma função "AssumeType (type, ...)" para sql_variants permita que eles sejam tratados com mais transparência.
declare @a bigint =
que você fez parece uma solução natural para mim, por que isso é inaceitável?
CONVERT()
em colunas e depois se juntar a eles. Isso certamente não é eficiente, na maioria dos casos. Neste particular, é apenas um valor a ser convertido, o que provavelmente não é um problema, mas que índices você tem na tabela? Os projetos de EAV geralmente têm bom desempenho, apenas com a indexação adequada (o que significa muitos índices nas tabelas geralmente estreitas).