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.
fonte
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).Respostas:
Não vou comentar sobre derramamentos, tempdb ou dicas, porque a consulta parece bem simples e precisa de muita consideração. Acho que o otimizador do SQL-Server fará seu trabalho muito bem, se houver índices adequados para a consulta.
E sua divisão em duas consultas é boa, pois mostra quais índices serão úteis. A primeira parte:
precisa de um índice para
(PropertyId, ObjectId, Sequence)
incluir oValue
. Eu faria issoUNIQUE
seguro. A consulta geraria erro de qualquer maneira durante o tempo de execução, se mais de uma linha fosse retornada; portanto, é bom garantir com antecedência que isso não aconteça, com o índice exclusivo:A segunda parte da consulta:
precisa de um índice para
(PropertyId, ObjectId)
incluirValue
:Se a eficiência não for aprimorada ou esses índices não forem usados ou ainda houver diferenças nas estimativas de linha aparecendo, será necessário examinar mais detalhadamente esta consulta.
Nesse caso, as conversões (necessárias do design do EAV e o armazenamento de diferentes tipos de dados nas mesmas colunas) são uma causa provável e sua solução de divisão (como comentam @AAron Bertrand e @Paul White) a consulta em duas partes parece natural e o caminho a percorrer. Uma reformulação para ter tipos de dados diferentes em suas respectivas colunas pode ser outra.
fonte
Como resposta parcial à pergunta explícita sobre como melhorar as estatísticas ...
Observe que as estimativas de linha, mesmo para o caso separado separadamente, ainda são 10X (4k vs 40k esperado).
O histograma de estatísticas provavelmente foi espalhado muito fino para essa propriedade porque é uma tabela longa (vertical) de 3,5 milhões de linhas e essa propriedade específica é extremamente esparsa.
Crie uma estatística adicional (um pouco redundante com as estatísticas IX) para a propriedade sparse:
Os originais:
Com convert () removido (adequado):
Com convert () removido (curto-circuito):
Ainda está desativado em ~ 2X, provavelmente porque> 99,9% dos objetos não têm a Propriedade 2840 definida neles. De fato, apenas para este caso de teste, a propriedade existe apenas em um dos 200k Objetos distintos da tabela de linhas de 3.5M. É incrível que tenha chegado tão perto realmente. Ajustando o filtro para ter menos ObjectIds,
Hmm, nenhuma alteração ... Apoiou isso adicionado "com verificação completa" ao final das estatísticas (pode ser por isso que os dois anteriores não funcionaram) e sim:
Yay. Portanto, em uma tabela altamente vertical com um IX abrangente, a adição de estatísticas filtradas adicionais parece ser uma grande melhoria (principalmente para combinações de teclas esparsas, mas altamente variantes).
fonte