web-dev-qa-db-fra.com

Sélection et insertion dans un serveur lié

Je rencontre une situation avec un serveur lié que je ne peux pas comprendre.

Nous avons donc un serveur lié d'un serveur 2008R2 à un serveur 2014. L'exemple de requête ci-dessous s'exécute à partir du serveur 2008R2 et fonctionne correctement.

SET XACT_ABORT ON;
 Declare @BatchSize int = 10
      DELETE from LINKEDSRV.DB.DBO.Table
      INSERT INTO LINKEDSRV.DB.DBO.Table (ECN)
      SELECT TOP (10) C1 from LINKEDSRV.DB.DBO.Table22 --order by C1
      SELECT * FROM LINKEDSRV.DB.DBO.Table

Mais quand j'exécute la même chose avec le order by C1 Cela ne renvoie aucun résultat.

Deuxième cas - Si je remplace la TOP(10) par TOP(@BatchSize) et non order by Aussi je ne reçois aucun résultat. par exemple

SELECT TOP (@BatchSize) C1 from LINKEDSRV.DB.DBO.Table22 

Tous les scénarios fonctionnent si je SET XACT_ABORT OFF. Est-ce que XACT_ABORT a une sorte de restriction en ce qui concerne le serveur lié?

Edit - A fait quelques tests supplémentaires et il semble que cela doive aussi faire quelque chose avec le nombre de lignes

Repo possible

Sur le serveurA

use testdb
go
create table t1( c1 int, c2 datetime)
create table t2( c1 int, c2 datetime)

insert into t2 select 1, GETDATE()
insert into t2 select  * from t2 -- insert close to 5000 rows, I found the issue around over around 35000 rows

Sur ServerB

Créer un serveur lié à ServerA

SET XACT_ABORT ON;
Declare @BatchSize int = 10
delete from ServerA.testdb.dbo.t1
insert into ServerA.testdb.dbo.t1 (c1) 
select top (@BatchSize) c1 from ServerA.testdb.dbo.t2 --order by c2
select * from ServerA.testdb.dbo.t1

Aucune sortie. Mais si vous réduisez le nombre de lignes dans le tableau t2 pour dire environ 2000, cela fonctionne très bien.

4
jesijesi

Tous les scénarios fonctionnent si je SET XACT_ABORT OFF. XACT_ABORT a une sorte de restriction en ce qui concerne le serveur lié?

Le réglage de XACT_ABORT est propagé au serveur SQL distant, comme indiqué dans Gestion des erreurs dans les procédures stockées à distance de serveur à serveur .

Le paramètre affecte également si les mises à jour sont possibles et comment elles sont gérées, comme indiqué dans Requêtes distribuées et transactions distribuées . Votre INSERT est autorisé avec XACT_ABORT OFF car SQL Server prend en charge les transactions imbriquées.

Néanmoins, il semble y avoir un bogue d'implémentation, car le suivi de l'activité sur le serveur 2014 lors de l'insertion révèle qu'une erreur se produit lorsque SQL Server tente de libérer un verrou de schéma:

trace excerpt

Cette erreur annule l'instruction (tentative de libération du verrou de schéma) sur le serveur distant, mais lorsque XACT_ABORT est OFF, le serveur distant continue de traiter l'instruction suivante. L'insertion se termine, malgré l'erreur de libération du verrouillage du schéma.

Quand XACT_ABORT est ON, tout le lot distant est abandonné, donc l'insertion est annulée.

J'ai pu reproduire votre problème localement, mais le ORDER BY la clause n'était pas significative.

Vous pouvez éviter le problème de plusieurs manières, notamment en enveloppant le INSERT dans une transaction explicite (en supposant que DTC soit disponible).

Je vous recommande d'éviter la syntaxe de nom en quatre parties pour les modifications à distance, car l'implémentation est basée sur un modèle de curseur. Vous obtiendrez normalement de meilleures performances en utilisant une méthode en bloc, ou en en tirant les données sur le serveur distant (plutôt que en le poussant depuis le serveur local).

Voir les questions et réponses connexes Laquelle est la plus efficace: sélectionner à partir du serveur lié ou insérer dans le serveur lié?

3
Paul White 9

D'après mon expérience, l'exécution de commandes en plusieurs parties via un serveur lié est généralement confrontée à des problèmes, en particulier lorsque les tables sont grandes. Essaye ça :

SET XACT_ABORT ON;
Declare @BatchSize int = 10
delete from ServerA.testdb.bbo.t1
select top (@BatchSize) c1 INTO #t2temp from ServerA.testdb.dbo.t2
insert into ServerA.testdb.dbo.t1 (c1) 
SELECT * FROM #t2temp
select * from ServerA.testdb.dbo.t1
1
Ehsan Pourtorab