Mise à jour de la requête sur une table MySQL liée à partir de SQL Server

J'ai un MS SQL Server avec un server MySQL lié. J'ai besoin de synchroniser partiellement une table entre les deux servers. Cela se fait en trois étapes et en fonction d'une condition:

  1. Supprimer toutes les lignes de la table MySQL qui ne satisfont pas la condition

  2. Insérer toutes les nouvelles lignes dans la table MySQL qui satisfont la condition

  3. Mettre à jour toutes les lignes du server MySQL qui satisfont la condition et ont des données différentes entre MySQL et SQL Server

Les étapes 1 et 2 fonctionnent toujours sans problème. Mais l'étape 3 ne fonctionnera pas s'il y a quelque chose à mettre à jour. La requête échoue avec l'exception suivante: L'set de lignes utilisait une concurrency optimiste et la valeur d'une colonne a été modifiée après que la ligne contenant ait été récupérée ou resynchronisée.

C'est la requête qui est exécutée:

update mysqlserver...subscribers set Firstname = Voornaam, Middlename = Tussenvoegsel, Surname = Achternaam, email = e-mail from mysqlserver...subscribers as b, tblkandidaat where (b.kandidaatid = tblkandidaat.kandidaatid) and (tblkandidaat.kandidaatid in ( select subsc.kandidaatid from mysqlserver...subscribers subsc inner join tblKandidaat on (subsc.kandidaatid=tblKandidaat.kandidaatid) where (subsc.list=1) and ((subsc.firstname COLLATE Latin1_General_CI_AI <> Voornaam or (subsc.middlename COLLATE Latin1_General_CI_AI <> Tussenvoegsel) or (subsc.surname COLLATE Latin1_General_CI_AI <> tblKandidaat.Achternaam) or (subsc.email COLLATE Latin1_General_CI_AI <> tblKandidaat.e-mail)) )); 

Quelqu'un a une idée sur la façon d'éviter cela?

Essayez cette requête à la place:

 update b set Firstname = Voornaam, Middlename = Tussenvoegsel, Surname = Achternaam, email = e-mail from mysqlserver...subscribers b inner join tblkandidaat k on b.kandidaatid = k.kandidaatid where b.list=1 and ( b.firstname COLLATE Latin1_General_CI_AI <> k.Voornaam or b.middlename COLLATE Latin1_General_CI_AI <> k.Tussenvoegsel or b.surname COLLATE Latin1_General_CI_AI <> k.Achternaam or b.email COLLATE Latin1_General_CI_AI <> ke-mail ) 
  1. Il est recommandé d'utiliser des jointures ANSI et de séparer correctement les conditions JOIN des conditions WHERE.

  2. Il est plus facile d'utiliser des alias pour toutes vos tables plutôt que des noms de table longs dans la requête.

  3. Il est préférable d'utiliser les alias pour toutes les references de colonne au lieu de les laisser vides. Non seulement c'est une bonne habitude et cela rend les choses plus claires, mais cela permet d'éviter de très mauvaises erreurs dans les references internes et externes.

  4. Si les performances sont également un problème: les jointures de server lié sont parfois dévolues au traitement ligne par ligne dans le moteur du fournisseur de données DB. J'ai trouvé des cas où rompre une partie d'un joint complexe à travers un server lié dans une jointure régulière suivie d'une application croisée réduit énormément les rangées inutiles qui sont récupérées et améliore considérablement les performances. (Cela consistait essentiellement en une search de signet, alias un balayage d'index non clusterisé suivi d'un index clusterisé en utilisant ces valeurs). Bien que cela ne soit pas parfaitement compatible avec le fonctionnement de MySql, cela vaut la peine d'expérimenter. Si vous pouvez faire n'importe quel type de trace pour voir les requêtes réelles effectuées du côté MySql, vous pourriez avoir un aperçu des autres methods à utiliser pour améliorer les performances.

  5. Une autre idée d'amélioration des performances consiste à copyr localement datatables distantes dans une table temporaire et à append une colonne ActionRequired. Ensuite, mettez à jour la table temporaire de façon à ce qu'elle apparaisse, en plaçant 'U', 'I' ou 'D' dans ActionRequired, puis effectuez la fusion / upsert sur le server lié avec un simple equijoins sur la key primaire, en utilisant ActionRequired. Une attention particulière aux conditions de concurrency possibles où la database distante pourrait être mise à jour pendant le traitement est en ordre.

  6. Méfiez-vous des nulls … sont toutes ces colonnes que vous comparez non-nullable?

Vous pourriez essayer de créer une deuxième table dans mysql, en faisant une insertion de sql-server dans cette table vide pour toutes les lignes changées et en faisant l'étape 3 entre les deux tables de mysql.

essayez de ne pas utiliser de sous-requête dans votre instruction where. Sous-requête peut renvoyer plus d'une ligne, puis vous avez l'erreur.

essayez de créer une vue dont la colonne source, destination et has_changed est située entre et dont les tables liées sont jointes. alors vous pouvez lancer une requête

mise à jour vi_upd_linkedtable set destination = source où has_changed = 1

Ceci est un tir dans le noir, mais essayez d'append FOR UPDATE ou LOCK IN SHARE MODE à la fin de votre requête sous-sélection. Cela indiquera à MySQL que vous essayez de sélectionner des éléments pour une mise à jour dans votre transaction et que vous devez créer un verrou d'écriture au niveau de la ligne pendant la select plutôt que pendant la update .

De 13.2.8.3. SELECT … FOR UPDATE et SELECT … VERROUILLER LE MODE SHARE Locking Lit :

SELECT … LOCK IN SHARE MODE définit un verrou de mode partagé sur les lignes lues. Un verrou de mode partagé permet à d'autres sessions de lire les lignes mais pas de les modifier. Les lignes lues sont les dernières disponibles, donc si elles appartiennent à une autre transaction qui n'a pas encore été validée, la lecture se bloque jusqu'à la fin de cette transaction.

Pour les lignes où les noms sont les mêmes, la update est une no-op.

Vous ne sauvegardez aucun travail en essayant de filterr les lignes où elles sont identiques, car datatables doivent encore être comparées à travers le lien. Donc, je ne vois aucun avantage pour la sous-requête.

Par conséquent, la requête peut être simplifiée beaucoup:

 update mysqlserver...subscribers set Firstname = Voornaam, Middlename = Tussenvoegsel, Surname = Achternaam, email = e-mail from mysqlserver...subscribers as b join tblkandidaat on b.kandidaatid = tblkandidaat.kandidaatid; where b.list = 1; 

L'élimination de la sous-requête pourrait faire disparaître votre problème de locking. MySQL a quelques problèmes combinant une select et une update à update sur la même table dans une requête donnée.

Essaye ça. J'ai écrit plusieurs d'entre eux aujourd'hui.

 update b set Firstname = Voornaam, Middlename = Tussenvoegsel, Surname = Achternaam, email = e-mail from mysqlserver...subscribers b inner join tblkandidaat k on b.kandidaatid = k.kandidaatid where b.list=1 and ( ISNULL(b.firstname,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Voornaam,'') or ISNULL(b.middlename,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Tussenvoegsel,'') or ISNULL(b.surname,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Achternaam,'') or ISNULL(b.email,'') COLLATE Latin1_General_CI_AI <> ISNULL(ke-mail,'') ) 

L'utilisation de l'ISNULL vous permet d'annuler vos colonnes.