À mon bureau, nous avons une requête qui est assez moche, mais qui fonctionne plutôt bien en production et dans l'environnement de développement (20sec et 4sec respectivement). Cependant, dans notre environnement de test, cela prend plus de 4 heures. SQL2005 (+ derniers correctifs) est en cours de production et de développement. SQL2008R2 est en cours d'exécution dans les tests.
J'ai jeté un coup d'œil au plan de requête, et il montre que SQL2008R2 utilise TempDB, au moyen d'un spouleur de table (spool paresseux) pour stocker les lignes renvoyées du serveur lié. L'étape suivante montre que les boucles imbriquées (anti-semi-jointure gauche) absorbent 96,3% de la requête. La ligne entre les deux opérateurs est à 5 398 Mo!
Le plan de requête pour SQL 2005 ne montre aucune utilisation de tempdb et aucune utilisation d'une anti-semi-jointure gauche.
Ci-dessous, le code aseptisé et les plans d'exécution, le plan 2005 en haut, le 2008R2 en bas.
Qu'est-ce qui cause le ralentissement et le changement drastiques? Je m'attendais à voir un plan d'exécution différent, donc cela ne me dérange pas. Le ralentissement spectaculaire du temps de requête est ce qui me trouble.
Dois-je regarder le matériel sous-jacent, puisque la version 2008R2 utilise tempdb, dois-je voir comment optimiser l'utilisation de cela?
Existe-t-il une meilleure façon d'écrire la requête?
Merci pour l'aide.
INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT
Table1.iGroupID,
GETDATE()
FROM Table1
WHERE
NOT EXISTS (
SELECT 1
FROM LinkedServer.Database.Table2 Alias2
WHERE
(
Alias2.FirstName + Alias2.LastName = dbo.fnRemoveNonLetter(Table1.FullName)
AND NOT dbo.fnRemoveNonLetter(Table1.FullName) IS NULL
AND NOT Alias2.FirstName IS NULL
AND NOT Alias2.LastName IS NULL
) OR (
Alias2.FamilyName = dbo.fnRemoveNonLetter(Table1.FamilyName)
AND Alias2.Child1Name = dbo.fnRemoveNonLetter(Table1.Child1Name)
AND NOT dbo.fnRemoveNonLetter(Table1.FamilyName) IS NULL
AND NOT dbo.fnRemoveNonLetter(Table1.Child1Name) IS NULL
AND NOT Alias2.Familyname IS NULL
AND NOT Alias2.Child1Name IS NULL
) OR (
Alias2.StepFamilyName = dbo.fnRemoveNonLetter(Table1.StepFamilyName)
AND Alias2.StepFamilyNameChild1 = dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2)
AND NOT Alias2.StepFamilyName IS NULL
AND NOT Alias2.StepFamilyNameChild1 IS NULL
AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyName) IS NULL
AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2) IS NULL
)
) AND NOT EXISTS (
SELECT 1
FROM Table3
INNER JOIN Table4
ON Table4.FirstNameType = Table3.FirstNameType
INNER JOIN table5
ON table5.LastNameType = Table3.LastNameType
WHERE
Table3.iGroupID = Table1.iGroupID
AND Table3.bIsClosed = 0
AND Table4.sNameTypeConstant = 'new_lastname'
AND table5.sFirstNameConstant = 'new_firstname'
)
:: EDIT :: Exécuté la requête à partir d'une instance SQL2005 différente, à peu près le même plan d'exécution que le "bon". Vous ne savez toujours pas comment les deux versions 2005 fonctionnent mieux sur le serveur lié 2008R2, que les instances 2008R2 vers les instances 2008R2.
Bien que je ne nie pas que le code puisse utiliser certains travaux, si c'était le code qui posait problème, ne verrais-je pas les mêmes plans d'exécution dans tous mes essais? Quelle que soit la version de SQL?
:: EDIT :: J'ai appliqué SP1 et CU3 aux deux instances 2008R2, toujours pas de dés. J'ai spécifiquement défini la collocation sur le serveur lié, pas de dés. J'ai spécifiquement défini les autorisations de mon compte utilisateur pour qu'il soit administrateur système sur les deux instances, pas de dés. Je me suis également souvenu de mes internes et du dépannage de sql server 2008, nous verrons si je peux suivre cela de quelque façon.
Merci à tous pour l'aide et les conseils.
:: EDIT :: J'ai apporté diverses modifications aux autorisations sur le serveur lié. J'ai utilisé des connexions SQL, des connexions de domaine, j'ai emprunté des utilisateurs, j'ai utilisé l'option "être créé en utilisant ce contexte de sécurité". J'ai créé des utilisateurs des deux côtés du serveur lié qui ont des droits d'administrateur système sur le serveur. Je n'ai plus d'idées.
Je voudrais toujours savoir pourquoi SQL2005 exécute la requête si radicalement différente de SQL2008R2. Si c'était la requête qui était mauvaise, je verrais le temps d'exécution de 4 + heures sur SQL2005 et SQL2008R2.
la source
+1 sur le commentaire Réécrire le commentaire de votre requête à partir du datagod.
Je me demande également si vous rencontrez un problème d'autorisations côté serveur lié conduisant à ce ralentissement. J'ai blogué sur ce ralentissement du serveur lié il y a un moment. Peut-être vaut-il la peine de vérifier les perms (est-ce un serveur lié SQL? Ou est-ce un autre SGBD? Si ce dernier, vous n'obtiendrez pas de bonnes statistiques de toute façon)
Avez-vous encore SQL Server 2005 sur l'environnement de test pour essayer cette requête et exclure l'environnement?
Avez-vous reconstruit les statistiques depuis la mise à niveau?
la source
Il y a tellement de problèmes avec cette comparaison ... Je ne sais pas par où commencer.
Obtenez les spécifications exactes de vos machines de production et de test.
Déterminez les liens réseau entre les différents serveurs liés dans les deux environnements. Sont-ils à la même vitesse? Les serveurs sont-ils situés côte à côte dans les deux environnements?
Existe-t-il un moyen quelconque de réécrire la requête pour NE PAS utiliser de serveurs liés? Rejoindre des tables sur des serveurs vous rend vulnérable aux changements de topologie, et son horriblement lent dans la plupart des cas.
L'utilisation de NOT et OR conduit généralement à des analyses de table complètes. Essayez de réécrire la requête.
la source