Sur un serveur de 32 Go, nous exécutons SQL Server 2014 SP2 avec une mémoire maximale de 25 Go, nous avons deux tables, vous trouverez ici une structure simplifiée des deux tables:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
avec les index non cluster suivants:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
La base de données est configurée avec compatibility level
120.
Lorsque j'exécute cette requête, il y a des déversements tempdb
. Voici comment j'exécute la requête:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Si ne sélectionnez pas le [remark]
champ, aucun déversement ne se produit. Ma première réaction a été que les déversements se sont produits en raison du faible nombre de lignes estimées sur l'opérateur de boucle imbriquée.
J'ajoute donc 5 colonnes datetime et 5 entiers à la table des paramètres et les ajoute à mon instruction select. Lorsque j'exécute la requête, aucun déversement ne se produit.
Pourquoi les déversements se produisent-ils uniquement lorsque [remark]
est sélectionné? Cela a probablement quelque chose à voir avec le fait qu'il s'agit d'un varchar(max)
. Que puis-je faire pour éviter de renverser tempdb
?
L'ajout OPTION (RECOMPILE)
à la requête ne fait aucune différence.
la source
select r.id, LEFT(remark, 512)
(ou quelle que soit la longueur de la sous-chaîne sensible).Réponses:
Il y aura plusieurs solutions de contournement possibles ici.
Vous pouvez ajuster manuellement l'allocation de mémoire, bien que je n'irais probablement pas dans cette voie.
Vous pouvez également utiliser un CTE et TOP pour pousser le tri plus bas, avant de saisir la colonne de longueur maximale. Il ressemblera à quelque chose comme ci-dessous.
Preuve de concept dbfiddle ici . Des exemples de données seraient toujours appréciés!
Si vous voulez lire une excellente analyse de Paul White, lisez ici.
la source
Le déversement se produit lorsque vous incluez cette colonne, car vous n'obtenez pas suffisamment de mémoire pour les grandes données de chaîne en cours de tri.
Vous n'obtenez pas une allocation de mémoire suffisamment importante car le nombre réel de lignes est 10 fois supérieur au nombre estimé de lignes (1 302 réel contre 126 estimé).
Pourquoi le devis est-il désactivé? Pourquoi SQL Server pense-t-il qu'il n'y a qu'une seule ligne dans dbo.Settings avec un
resourceid
de 38?Il peut s'agir d'un problème de statistiques, que vous pouvez vérifier en exécutant
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
et voir les nombres pour cette étape d'histogramme. Mais le plan d'exécution semble indiquer que les statistiques sont aussi complètes et à jour qu'elles pourraient l'être.Étant donné que les statistiques ne sont pas utiles, votre meilleur pari est probablement une réécriture de la requête - que Forrest a couverte dans sa réponse.
la source
Pour moi, il semble que la
where
clause de la requête pose problème et soit la cause des faibles estimations, même si elleOPTION(RECOMPILE)
est utilisée.J'ai créé quelques données de test, et finalement j'ai trouvé deux solutions, en stockant le
ID
champ à partirresources
d'une variable (si elle est toujours unique) ou d'une table temporaire, si nous pouvons en avoir plus d'uneID
.Enregistrements de test de base
Insérez les valeurs «Seek» pour obtenir le même jeu de résultats approximatif que OP (1300 enregistrements)
Modifier les statistiques de compatibilité et de mise à jour pour correspondre à OP
Requête d'origine
Mes estimations sont encore pires , avec une ligne estimée, tandis que 1300 sont retournées. Et comme OP l'a dit, peu importe si j'ajoute
OPTION(RECOMPILE)
Une chose importante à noter est que lorsque nous nous débarrassons de la clause where, les estimations sont correctes à 100%, ce qui est attendu car nous utilisons toutes les données dans les deux tableaux.
J'ai forcé les index juste pour m'assurer que nous utilisons les mêmes que dans la requête précédente, pour prouver le point
Comme prévu, de bonnes estimations.
Alors, que pourrions-nous changer pour obtenir de meilleures estimations tout en recherchant nos valeurs?
SI @UID est unique, comme dans l'exemple donné par OP, nous pourrions mettre le single
id
qui a été renvoyéresources
dans une variable, puis rechercher cette variable avec une OPTION (RECOMPILE)Ce qui donne des estimations précises à 100%
Mais que se passe-t-il s'il y a plusieurs resourceUID dans les ressources?
ajouter des données de test
Cela pourrait être résolu avec une table temporaire
Encore une fois avec des estimations précises .
Cela a été fait avec mon propre jeu de données, YMMV.
Écrit avec sp_executesql
Avec une variable
Avec une table temporaire
100% toujours des estimations correctes sur mon test
la source