No SQL Server 2016 SP2, temos uma consulta que tem uma estimativa muito baixa no operador de loop aninhado. Devido à estimativa baixa, essa consulta também é derramada no tempdb.
Se estiver correto, o SQL Server 2014+ usa a estimativa de histograma grosseiro para calcular o número estimado de linhas em uma junção.
Mas quando executo a consulta, o SQL Server usa o vetor de densidade para calcular o número de linhas estimadas.
O SQL Server está usando apenas a estimativa de histograma grosseiro se não houver nenhuma where
cláusula?
Normalmente eu usaria estatísticas filtradas para melhorar as estimativas quando tenho uma tabela com dados distorcidos. Mas neste caso isso não parece funcionar.
Existe uma maneira de melhorar as estimativas no loop aninhado?
Usando o código a seguir, você pode reproduzir os dados:
create table MyTable
(
id int identity,
field varchar(50),
constraint pk_id primary key clustered (id)
)
go
create table SkewedTable
(
id int identity,
startdate datetime,
myTableId int,
remark varchar(50),
constraint pk_id primary key clustered (id)
)
set nocount on
insert into MyTable select top 1000 [name] from master..spt_values
go
insert into SkewedTable select GETDATE(),FLOOR(RAND()*(1000))+1,REPLICATE(N'A',FLOOR(RAND()*(40))+1)
go 1000
insert into SkewedTable select GETDATE(),FLOOR(RAND()*(1000))+1,REPLICATE(N'A',FLOOR(RAND()*(40))+1)
go
CREATE NONCLUSTERED INDEX [ix_field] ON [dbo].[MyTable]([field] ASC)
go
CREATE NONCLUSTERED INDEX [ix_mytableid] ON [dbo].[SkewedTable]([myTableId] ASC)
go
--95=varchar in sys.messages
set nocount off
;with cte as
(
select GETDATE() as startdate ,95 as myTableId, REPLICATE(N'B',FLOOR(RAND()*(40))+1) as remark
union all
select * from cte
)
insert into skewedtable select top 40000 * from cte
option(maxrecursion 0)
go
update statistics mytable with fullscan
go
update statistics skewedtable with fullscan
go
Você deve achar útil a seguinte estatística filtrada:
Isso fornece ao otimizador informações sobre a distribuição de
id
valores que correspondemfield = 'varchar'
a , fornecendo uma estimativa de seletividade muito melhor para a junção:O plano de execução acima mostra estimativas exatamente corretas com a estatística filtrada, levando o otimizador a escolher uma junção de hash (por motivos de custo).
Esta informação de distribuição é muito mais importante do que o método exato usado pelo estimador para combinar os histogramas de junção ( alinhamento fino ou grosseiro ), ou mesmo as suposições gerais (por exemplo, junção simples, contenção de base).
Se você não puder fazer isso, suas opções são amplamente descritas na resposta à sua pergunta anterior Classificar spills to tempdb devido a varchar(max) . Minha preferência provavelmente seria uma tabela temporária intermediária.
Concordo completamente com o índice filtrado, esta resposta é adicionada para expandir a outra opção que @PaulWhite mencionou, para usar uma tabela temporária intermediária e, consequentemente, se livrar do
SORT
operadorVocê pode adicionar um índice ou alterar o índice existente:
Insira os valores em uma tabela temporária intermediária
Adicionar um índice na tabela temporária
E, em seguida, use um CTE para remover o operador de classificação do plano de consulta
Conforme mencionado por @Forrest para diminuir a classificação aqui
Resultado:
O que remove o operador de classificação.