Question

J'ai écrit un processus stocké qui effectuera une mise à jour si un enregistrement existe, sinon il effectuera une insertion.Cela ressemble à ceci :

update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)

Ma logique derrière l'écriture de cette manière est que la mise à jour effectuera une sélection implicite à l'aide de la clause Where et si celle-ci renvoie 0, l'insertion aura lieu.

L'alternative à procéder de cette façon serait d'effectuer une sélection, puis, en fonction du nombre de lignes renvoyées, d'effectuer une mise à jour ou une insertion.J'ai considéré cela comme inefficace car si vous devez effectuer une mise à jour, cela entraînera 2 sélections (le premier appel de sélection explicite et le second implicite dans le où de la mise à jour).Si le processus devait effectuer une insertion, il n'y aurait aucune différence d'efficacité.

Ma logique est-elle valable ici ?Est-ce ainsi que vous combineriez une insertion et une mise à jour dans un processus stocké ?

Était-ce utile?

La solution

Votre hypothèse est correcte, c'est la façon optimale de le faire et cela s'appelle insérer/fusionner.

Importance d'UPSERT - de sqlservercentral.com:

Pour chaque mise à jour du cas mentionné ci-dessus, nous supprimons une lecture supplémentaire du tableau si nous utilisons l'upsert au lieu d'existé.Malheureusement pour un insert, les méthodes upsert et si existent, utilisez le même nombre de lectures sur la table.Par conséquent, le contrôle de l'existence ne doit être effectué que lorsqu'il existe une raison très valable de justifier les E / S supplémentaires.La façon optimisée de faire les choses est de vous assurer que vous avez peu de lectures que possible sur la base de données.

La meilleure stratégie consiste à tenter la mise à jour.Si aucune ligne n'est affectée par la mise à jour, insérez.Dans la plupart des cas, la ligne existera déjà et une seule E / S sera nécessaire.

Modifier:Veuillez vérifier s'il vous plait cette réponse et le billet de blog lié pour en savoir plus sur les problèmes liés à ce modèle et comment le faire fonctionner en toute sécurité.

Autres conseils

Veuillez lire le poster sur mon blog pour un bon modèle sûr que vous pouvez utiliser.De nombreuses considérations entrent en jeu et la réponse acceptée à cette question est loin d’être sûre.

Pour une réponse rapide, essayez le modèle suivant.Cela fonctionnera bien sur SQL 2000 et supérieur.SQL 2005 vous propose une gestion des erreurs qui ouvre d'autres options et SQL 2008 vous propose une commande MERGE.

begin tran
   update t with (serializable)
   set hitCount = hitCount + 1
   where pk = @id
   if @@rowcount = 0
   begin
      insert t (pk, hitCount)
      values (@id,1)
   end
commit tran

S'il doit être utilisé avec SQL Server 2000/2005, le code d'origine doit être inclus dans la transaction pour garantir que les données restent cohérentes dans un scénario simultané.

BEGIN TRANSACTION Upsert
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
COMMIT TRANSACTION Upsert

Cela entraînera des coûts de performances supplémentaires, mais garantira l'intégrité des données.

Ajoutez, comme déjà suggéré, MERGE doit être utilisé lorsqu'il est disponible.

MERGE est d'ailleurs l'une des nouvelles fonctionnalités de SQL Server 2008.

Vous devez non seulement l’exécuter en transaction, mais il nécessite également un niveau d’isolation élevé.En fait, le niveau d'isolement par défaut est Read Commited et ce code doit être sérialisable.

SET transaction isolation level SERIALIZABLE
BEGIN TRANSACTION Upsert
UPDATE myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
  begin
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2)
  end
COMMIT TRANSACTION Upsert

Peut-être que l'ajout de la vérification et de la restauration @@error pourrait être une bonne idée.

Si vous n'effectuez pas de fusion dans SQL 2008, vous devez la modifier comme suit :

si @@rowcount = 0 et @@error=0

sinon, si la mise à jour échoue pour une raison quelconque, elle tentera ensuite une insertion car le nombre de lignes sur une instruction ayant échoué est 0

Grand fan de l'UPSERT, réduit vraiment le code à gérer.Voici une autre façon de procéder :L'un des paramètres d'entrée est ID, si l'ID est NULL ou 0, vous savez que c'est un INSERT, sinon c'est une mise à jour.Suppose que l'application sait s'il existe un identifiant, elle ne fonctionnera donc pas dans toutes les situations, mais réduira les exécutions de moitié si vous le faites.

Votre logique semble solide, mais vous voudrez peut-être envisager d'ajouter du code pour empêcher l'insertion si vous avez transmis une clé primaire spécifique.

Sinon, si vous effectuez toujours une insertion si la mise à jour n'a affecté aucun enregistrement, que se passe-t-il lorsque quelqu'un supprime l'enregistrement avant l'exécution de "UPSERT" ?Maintenant, l’enregistrement que vous essayiez de mettre à jour n’existe plus, il va donc créer un enregistrement à la place.Ce n'est probablement pas le comportement que vous recherchiez.

Message modifié de Dima Malenko :

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET    COL1 = @col1, 
       COL2 = @col2 
WHERE  ID = @ID 

IF @@rowcount = 0 
  BEGIN 
      INSERT INTO MYTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

IF @@Error > 0 
  BEGIN 
      INSERT INTO MYERRORTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

COMMIT TRANSACTION UPSERT 

Vous pouvez intercepter l'erreur et envoyer l'enregistrement vers une table d'insertion ayant échoué.
Je devais le faire parce que nous prenons toutes les données envoyées via WSDL et, si possible, les corrigeons en interne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top