J'ai deux serveurs de base de données, connectés via des serveurs liés. Les deux sont des bases de données SQL Server 2008R2 et la connexion au serveur lié est établie via un lien "SQL Server" normal, en utilisant le contexte de sécurité de la connexion actuelle. Les serveurs liés sont tous deux dans le même centre de données, donc la connexion ne devrait pas être un problème.
J'utilise la requête suivante pour vérifier quelles valeurs de la colonne identifier
sont disponibles à distance, mais pas localement.
SELECT
identifier
FROM LinkedServer.RemoteDb.schema.[TableName]
EXCEPT
SELECT DISTINCT
identifier
FROM LocalDb.schema.[TableName]
Sur les deux tables se trouvent des index non clusterisés sur la colonne identifier
. Localement, il y a environ 2,6 millions de lignes, à distance seulement 54. Pourtant, quand on regarde le plan de requête, 70% du temps d'exécution est consacré à "exécuter une requête distante". De plus, lorsque vous étudiez le plan de requête complet, le nombre de lignes locales estimées est 1
au lieu de 2695380
(qui est le nombre de lignes estimées lorsque vous sélectionnez uniquement la requête venant après EXCEPT
). Lors de l'exécution de cette requête, cela prend en effet beaucoup de temps.
Cela me fait me demander: pourquoi est-ce? Est-ce que l'estimation est "juste" loin ou les requêtes distantes sur des serveurs liés sont-elles vraiment si chères?
Le plan que vous avez en ce moment me semble être le plan le plus optimal.
Je ne suis pas d'accord avec l'affirmation dans les autres réponses selon laquelle il envoie les 2,6 millions de lignes au serveur distant.
Le plan me semble que pour chacune des 54 lignes renvoyées par la requête distante, il effectue une recherche d'index dans votre table locale pour déterminer si elle correspond ou non. C'est à peu près le plan optimal.
Le remplacement par une jointure de hachage ou une fusion de fusion serait contre-productif étant donné la taille de la table et l'ajout d'une table #temp
Intermédiaire ajoute simplement une étape supplémentaire qui ne semble pas vous donner d'avantage.
La connexion à une ressource distante coûte cher. Période.
L'une des opérations les plus coûteuses dans n'importe quel environnement de programmation est le réseau IO (bien que le disque IO a tendance à le faire éclipser)).
Cela s'étend aux serveurs liés distants. Le serveur appelant le serveur lié distant doit d'abord établir une connexion, puis une requête doit être exécutée sur le serveur distant, les résultats retournés et la connexion fermée. Tout cela prend du temps sur le réseau.
Vous devez également structurer votre requête de manière à transférer le minimum de données sur le câble. Ne vous attendez pas à ce que la base de données soit optimisée pour vous.
Si je devais écrire cette requête, je sélectionnerais les données distantes dans une variable de table (ou dans une table temporaire), puis l'utiliserais conjointement avec la table locale. Cela garantit que seules les données qui doivent être transférées le seront.
La requête que vous exécutez peut facilement envoyer 2,6 millions de lignes au serveur distant afin de traiter la clause EXCEPT
.
Je ne suis pas un expert mais si vous utilisez Union, Except ou Intersect, vous n'avez pas besoin d'utiliser "Distinct". Selon les valeurs de LocalDb.schema. [TableName], les performances de la requête peuvent être améliorées.
SELECT
identifier
FROM LinkedServer.RemoteDb.schema.[TableName]
EXCEPT
SELECT
identifier
FROM LocalDb.schema.[TableName]
Oded a raison, le problème de performances est dû à l'envoi des lignes 2,6 millions à votre serveur distant.
Pour résoudre ce problème, vous pouvez forcer l'envoi des données distantes (54 lignes) en utilisant une table temporaire ou en mémoire.
en utilisant une table temporaire
SELECT identifier
INTO #TableName
FROM LinkedServer.RemoteDb.schema.[TableName]
SELECT identifier
FROM #TableName
EXCEPT
SELECT DISTINCT identifier
FROM LocalDb.schema.[TableName]
DROP #TableName
Je pense que vous feriez mieux de répliquer la table distante sur le serveur à partir duquel vous interrogez, puis d'exécuter tout votre SQL localement.