web-dev-qa-db-fra.com

Insertion ou mise à jour T-SQL

J'ai une question concernant la performance de SQL Server.

Supposons que j'ai une table persons avec les colonnes suivantes: id, name, surname.

Maintenant, je veux insérer une nouvelle ligne dans cette table. La règle est la suivante:

  1. Si id n'est pas présent dans la table, puis insérez la ligne.

  2. Si id est présent, puis mettez à jour.

J'ai deux solutions ici:

D'abord:

update persons
  set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
if @@ROWCOUNT = 0 
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

Seconde:

if exists (select id from persons where id = @p_id)
  update persons
    set id=@p_id, name=@p_name, surname=@p_surname
  where id=@p_id
else
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

Quelle est une meilleure approche? Cela semble être dans le deuxième choix, de mettre à jour une ligne, il doit être fouillé deux fois, alors que dans la première option - une seule fois. Y a-t-il d'autres solutions au problème? J'utilise MS SQL 2000.

36
Markus

Les deux fonctionnent bien, mais j'utilise habituellement l'option 2 (pré-MSSQL 2008) puisqu'elle lit un peu plus clairement. Je ne stresserais pas la performance ici non plus ... Si cela devient un problème, vous pouvez utiliser NOLOCK dans la clause exists. Bien que avant de commencer à utiliser Nolock partout, assurez-vous d'avoir couvert toutes vos bases (index et architecture de grandes images). Si vous savez que vous mettrez à jour chaque article plus d'une fois, vous pourriez payer pour envisager l'option 1.

L'option 3 est de ne pas utiliser de mises à jour destructives. Cela prend plus de travail, mais essentiellement, vous insérez une nouvelle ligne chaque fois que les données changent (ne mettent jamais à jour ni ne suppriment de la table) et ont une vue qui sélectionne toutes les lignes les plus récentes. C'est utile si vous voulez que la table contienne une histoire de tous ses états précédents, mais cela peut également être surchargé.

11
dan

L'option 1 semble bonne. Toutefois, si vous êtes sur SQL Server 2008, vous pouvez également utiliser FUSIONNEZ , qui peut fonctionner correctement pour de telles tâches upentes.

Notez que vous souhaiterez peut-être utiliser une transaction explicite et l'option xact_abort pour ces tâches, de sorte que la cohérence des transactions reste en cas de problème ou d'un changement concomitable.

12
Lucero

J'ai tendance à utiliser l'option 1. S'il y a enregistré dans une table, vous enregistrez une recherche. S'il n'y a pas, vous ne perdez rien. De plus, dans la deuxième option, vous pouvez rencontrer des problèmes de verrouillage et de blocage amusant liés à une incompatibilité des serrures. Il y a plus d'informations sur mon blog:

http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

4
Piotr Rodak

Viser à être un peu plus secs, j'évite écrire deux fois la liste des valeurs.

begin tran
insert into persons (id)
select @p_id from persons
 where not exists (select * from persons where id = @p_id)

update persons
set name=@p_name, surname=@p_surname
where id = @p_id

commit

Colonnes name et surname doivent être nullables.

La transaction signifie qu'aucun autre utilisateur ne verra jamais l'enregistrement "vide".

Modifier: Nettoyage

3
Patrick