Sur SQL Server 2016 SP2, nous avons une Query qui a une estimation très faible sur l'opérateur de boucle imbriquée. En raison de l'estimation faible, cette requête se répand également à TEMPDB.
Si je suis correct SQL Server 2014+ Utilisations Estimation de l'histogramme grossière Pour calculer le nombre estimé de lignes sur une jointure.
[.____] Mais lorsque j'exécute la requête, SQL Server utilise le Vecteur de densité Pour calculer le nombre de lignes estimées.
[.____] Est-ce que SQL Server n'utilise que l'estimation l'estimation de l'histogramme grossière S'il n'y a pas where
clause?
Normalement, j'utiliserais Statistiques filtrées Pour améliorer les estimations lorsque j'ai une table avec des données asymétriques. Mais dans ce cas qui ne semble pas fonctionner.
Existe-t-il un moyen d'améliorer les estimations sur la boucle imbriquée?
En utilisant le code suivant, vous pouvez reproduire les données:
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
Normalement, j'utiliserais Statistiques filtrées Pour améliorer les estimations lorsque j'ai une table avec des données asymétriques. Mais dans ce cas qui ne semble pas fonctionner.
Vous devriez trouver la statistique filtrée suivante utile:
CREATE STATISTICS [stats id (field=varchar)]
ON dbo.MyTable (id)
WHERE field = 'varchar'
WITH FULLSCAN;
Cela donne aux informations d'optimisation sur la distribution de id
valeurs que correspondezfield = 'varchar'
, donner une estimation bien meilleure sélectivité pour la jointure:
Le Execution Plan ci-dessus montre exactement les estimations correctes avec la statistique filtrée, ce qui a conduit l'optimiseur à choisir une jointure de hachage (pour des raisons de coût).
Ceci Distribution Les informations sont beaucoup plus importantes que la méthode exacte utilisée par l'estimateur pour correspondre aux histogrammes de jointure (fine ou alignement grossier ), ou même les hypothèses générales (par exemple, une jointure simple, un confinement de base).
Si vous ne pouvez pas le faire, vos options sont largement décrites dans la réponse à votre question précédente Trier les déversements sur Tempdb en raison de Varchar (max) . Ma préférence serait probablement une table temporaire intermédiaire.
Tout d'accord avec l'index filtré, cette réponse est ajoutée pour développer sur l'autre option que @Paulwhite mentionnée, pour utiliser une table tempétrée intermédiaire et se débarrasser de l'opérateur SORT
Vous pouvez ajouter un index ou modifier l'index existant:
CREATE INDEX IX_SkewedTable_MytableId_startdate
ON SkewedTable(myTableId,startdate)
INCLUDE(remark);
Insérez les valeurs dans une table de température intermédiaire
CREATE TABLE #temp2(param int);
INSERT INTO #temp2(param)
SELECT t.id
FROM mytable t
WHERE t.field = 'varchar';
Ajouter un index sur la table Temp
CREATE INDEX IX_ID on #temp2(param);
puis utilisez un CTE pour supprimer l'opérateur de tri du plan de requête
;WITH CTE AS
(
select TOP(999999999999)
s.myTableId,s.id,s.remark from
SkewedTable s
order by startdate
)
SELECT s.id , s.remark
from CTE s
INNER JOIN #temp2
on s.myTableId = #temp2.param
OPTION(RECOMPILE)
Comme mentionné par @Forrest pour pousser le tri inférieur ici
Résultat:
qui supprime l'opérateur de tri.