Solutions pour INSÉRER OU METTRE À JOUR sur SQL Server

599

Supposons une structure de table de MyTable(KEY, datafield1, datafield2...).

Je souhaite souvent mettre à jour un enregistrement existant ou insérer un nouvel enregistrement s'il n'existe pas.

Essentiellement:

IF (key exists)
  run update command
ELSE
  run insert command

Quelle est la meilleure façon d'écrire cela?

Chris Cudmore
la source
28
Si vous rencontrez cette question pour la première fois, assurez-vous de lire toutes les réponses et leurs commentaires. L'âge peut parfois conduire à des informations trompeuses ...
Aaron Bertrand
1
Pensez à utiliser l'opérateur EXCEPT, qui a été introduit dans SQL Server 2005.
Tarzan

Réponses:

370

n'oubliez pas les transactions. Les performances sont bonnes, mais une approche simple (SI EXISTE ..) est très dangereuse.
Lorsque plusieurs threads tentent d'effectuer une insertion ou une mise à jour, vous pouvez facilement obtenir une violation de clé primaire.

Les solutions fournies par @Beau Crawford et @Esteban montrent une idée générale mais sujette aux erreurs.

Pour éviter les blocages et les violations de PK, vous pouvez utiliser quelque chose comme ceci:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

ou

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran
aku
la source
1
La question demandait la solution la plus performante plutôt que la plus sûre. Bien qu'une transaction ajoute de la sécurité au processus, elle ajoute également des frais généraux.
Luke Bennett
31
Ces deux méthodes peuvent toujours échouer. Si deux threads simultanés font de même sur la même ligne, le premier réussira, mais la deuxième insertion échouera en raison d'une violation de clé primaire. Une transaction ne garantit pas que l'insertion réussira même si la mise à jour a échoué car l'enregistrement existait. Pour garantir que n'importe quel nombre de transactions simultanées réussira, vous DEVEZ utiliser un verrou.
Jean Vincent
7
@aku une raison pour laquelle vous avez utilisé des conseils de table ("avec (xxxx)") par opposition à "FIXER LE NIVEAU D'ISOLEMENT DE TRANSACTION SÉRIALISABLE" juste avant votre BEGIN TRAN?
EBarr
4
@CashCow, le dernier gagne, c'est ce que INSERT ou UPDATE est censé faire: le premier insère, le second met à jour l'enregistrement. L'ajout d'un verrou permet que cela se produise dans un délai très court, empêchant une erreur.
Jean Vincent
1
J'ai toujours pensé que l'utilisation des astuces de verrouillage était mauvaise, et nous devrions laisser le moteur interne de Microsoft dicter les verrous. Est-ce là l'exception apparente à la règle?
382

Voir ma réponse détaillée à une question précédente très similaire

@Beau Crawford est un bon moyen dans SQL 2005 et ci-dessous, bien que si vous accordez un représentant, il devrait aller au premier gars pour le faire . Le seul problème est que pour les insertions, il s'agit toujours de deux opérations d'E / S.

MS SQL2008 introduit à mergepartir de la norme SQL: 2003:

merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

Maintenant, c'est vraiment juste une opération IO, mais un code affreux :-(

Keith
la source
10
@Ian Boyd - oui, c'est la syntaxe de la norme SQL: 2003, pas upsertcelle que presque tous les autres fournisseurs de bases de données ont décidé de prendre en charge à la place. La upsertsyntaxe est un moyen beaucoup plus agréable de le faire, donc à tout le moins, MS aurait dû le prendre en charge également - ce n'est pas comme si c'était le seul mot-clé non standard dans T-SQL
Keith
1
un commentaire sur l'indice de verrouillage dans d'autres réponses? (le découvrira bientôt, mais si c'est la voie recommandée, je recommande de l'ajouter à la réponse)
eglasius
25
Voir ici weblogs.sqlteam.com/dang/archive/2009/01/31/… pour une réponse sur la façon d'empêcher les conditions de concurrence de provoquer des erreurs qui peuvent se produire même lors de l'utilisation de la MERGEsyntaxe.
Seph
5
@Seph, c'est une vraie surprise - un échec de Microsoft là-bas: -SI suppose que cela signifie que vous avez besoin d'une HOLDLOCKopération de fusion dans des situations de concurrence élevée.
Keith
11
Cette réponse a vraiment besoin d'être mise à jour pour tenir compte du commentaire de Seph à propos du fait qu'il ne soit pas thread-safe sans HOLDLOCK. Selon l'article lié, MERGE supprime implicitement un verrou de mise à jour, mais le libère avant d'insérer des lignes, ce qui peut provoquer une condition de concurrence critique et des violations de clé primaire lors de l'insertion. En utilisant HOLDLOCK, les verrous sont conservés jusqu'à ce que l'insertion se produise.
Triynko
169

Faites un UPSERT:

UPDATE MyTable SET FieldA = @ FieldA WHERE Key = @ Key

SI @@ ROWCOUNT = 0
   INSÉRER DANS LES VALEURS MyTable (FieldA) (@FieldA)

http://en.wikipedia.org/wiki/Upsert

Beau Crawford
la source
7
Les violations de clé primaire ne devraient pas se produire si les contraintes d'index unique appropriées sont appliquées. L'intérêt de la contrainte est d'éviter que des lignes en double ne se produisent. Peu importe le nombre de threads essayant d'insérer, la base de données sera sérialisée selon les besoins pour appliquer la contrainte ... et si ce n'est pas le cas, le moteur ne vaut rien. Bien sûr, cela dans une transaction sérialisée rendrait cela plus correct et moins vulnérable aux blocages ou aux insertions défaillantes.
Triynko
19
@Triynko, je pense que Safran signifie que @ Sam si deux + fils entrelacent dans la séquence à droite , puis serveur sql jeter une erreur indiquant une violation de clé primaire aurait eu lieu. L'encapsuler dans une transaction sérialisable est la bonne façon d'éviter les erreurs dans l'ensemble d'instructions ci-dessus.
EBarr
1
Même si vous avez une clé primaire qui est une incrémentation automatique, votre préoccupation sera alors toute contrainte unique qui pourrait être sur la table.
Seph
1
la base de données doit prendre en charge les principaux problèmes clés. Ce que vous dites, c'est que si la mise à jour échoue et qu'un autre processus arrive en premier avec un insert, votre insert échouera. Dans ce cas, vous avez quand même une condition de concurrence. Le verrouillage ne changera pas le fait que la post-condition sera que l'un des processus qui essaient d'écrire obtiendra la valeur.
CashCow
93

Beaucoup de gens vous proposeront de l'utiliser MERGE, mais je vous déconseille. Par défaut, il ne vous protège pas plus de la concurrence et des conditions de concurrence que de multiples déclarations, et il présente d'autres dangers:

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

Même avec cette syntaxe "plus simple" disponible, je préfère toujours cette approche (gestion des erreurs omise pour plus de brièveté):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;

Beaucoup de gens vont suggérer de cette façon:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
  INSERT ...
END
COMMIT TRANSACTION;

Mais tout cela permet de s'assurer que vous devrez peut-être lire le tableau deux fois pour localiser la ou les lignes à mettre à jour. Dans le premier exemple, vous n'aurez besoin de localiser les lignes qu'une seule fois. (Dans les deux cas, si aucune ligne n'est trouvée lors de la lecture initiale, une insertion se produit.)

D'autres proposeront de cette façon:

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

Cependant, cela est problématique si, pour aucune autre raison que de laisser SQL Server intercepter les exceptions que vous auriez pu empêcher en premier lieu, c'est beaucoup plus cher, sauf dans le rare scénario où presque chaque insertion échoue. J'en prouve autant ici:

Aaron Bertrand
la source
3
Qu'en est-il de l'insertion / mise à jour d'une table de tem qui insère / met à jour de nombreux enregistrements?
user960567
@ user960567 Eh bien,UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Aaron Bertrand
4
nice a répondu après plus de 2 ans :)
user960567
12
@ user960567 Désolé, je ne reçois pas toujours les notifications de commentaires en temps réel.
Aaron Bertrand du
60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

Éditer:

Hélas, même à mon détriment, je dois admettre que les solutions qui le font sans sélection semblent être meilleures car elles accomplissent la tâche avec une étape de moins.

Esteban Araya
la source
6
J'aime toujours mieux celui-ci. L'upsert ressemble plus à une programmation par effet secondaire, et je n'ai jamais vu le petit index clusterisé de cette sélection initiale provoquer des problèmes de performances dans une vraie base de données.
Eric Z Beard,
38

Si vous souhaitez UPSERT plusieurs enregistrements à la fois, vous pouvez utiliser l'instruction MERGE ANSI SQL: 2003 DML.

MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

Consultez Mimicking MERGE Statement dans SQL Server 2005 .

Eric Weilnau
la source
1
Dans Oracle, l'émission d'une instruction MERGE, je pense, verrouille la table. La même chose se produit-elle dans SQL * Server?
Mike McAllister
13
MERGE est sensible aux conditions de concurrence (voir weblogs.sqlteam.com/dang/archive/2009/01/31/… ) à moins que vous ne lui imposiez des verrous certians. Jetez également un œil aux performances de MERGE dans SQL Profiler ... je trouve qu'elles sont généralement plus lentes et génèrent plus de lectures que les solutions alternatives.
EBarr
@EBarr - Merci pour le lien sur les serrures. J'ai mis à jour ma réponse pour inclure l'indication de verrouillage suggérée.
Eric Weilnau
Consultez également mssqltips.com/sqlservertip/3074/…
Aaron Bertrand
10

Bien qu'il soit assez tard pour commenter cela, je veux ajouter un exemple plus complet en utilisant MERGE.

Ces instructions Insert + Update sont généralement appelées instructions "Upsert" et peuvent être implémentées à l'aide de MERGE dans SQL Server.

Un très bon exemple est donné ici: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

Ce qui précède explique également les scénarios de verrouillage et de concurrence.

Je citerai la même chose pour référence:

ALTER PROCEDURE dbo.Merge_Foo2
      @ID int
AS

SET NOCOUNT, XACT_ABORT ON;

MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
      ON f.ID = new_foo.ID
WHEN MATCHED THEN
    UPDATE
            SET f.UpdateSpid = @@SPID,
            UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
    INSERT
      (
            ID,
            InsertSpid,
            InsertTime
      )
    VALUES
      (
            new_foo.ID,
            @@SPID,
            SYSDATETIME()
      );

RETURN @@ERROR;
user243131
la source
1
Il y a d'autres choses à craindre avec MERGE: mssqltips.com/sqlservertip/3074/…
Aaron Bertrand
8
/*
CREATE TABLE ApplicationsDesSocietes (
   id                   INT IDENTITY(0,1)    NOT NULL,
   applicationId        INT                  NOT NULL,
   societeId            INT                  NOT NULL,
   suppression          BIT                  NULL,
   CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/

DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0

MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
    AS source (applicationId, societeId, suppression)
    --here goes the ON join condition
    ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
    UPDATE
    --place your list of SET here
    SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
    --insert a new line with the SOURCE table one row
    INSERT (applicationId, societeId, suppression)
    VALUES (source.applicationId, source.societeId, source.suppression);
GO

Remplacez les noms de table et de champ par tout ce dont vous avez besoin. Prenez soin de l' utilisation de la condition ON . Définissez ensuite la valeur appropriée (et le type) pour les variables sur la ligne DECLARE.

À votre santé.

Denver
la source
7

Vous pouvez utiliser MERGEStatement, This statement is used to insert data if not exist or update if does exist.

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`
Daniel Acosta
la source
@RamenChef je ne comprends pas. Où sont les clauses WHEN MATCHED?
likejudo
@likejudo Je n'ai pas écrit ceci; Je l'ai seulement révisé. Demandez à l'utilisateur qui a écrit le message.
RamenChef
5

Si vous passez à la route UPDATE if-no-rows-updated puis INSERT, envisagez de faire l'insertion en premier pour éviter une condition de concurrence (en supposant qu'aucune intervention ne soit SUPPRIMÉE)

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

En plus d'éviter une condition de concurrence, si dans la plupart des cas l'enregistrement existe déjà, cela entraînera l'échec de INSERT, gaspillant le processeur.

L'utilisation de MERGE est probablement préférable pour SQL2008 et ultérieur.

Kristen
la source
Idée intéressante, mais syntaxe incorrecte. Le SELECT a besoin d'un FROM <table_source>, et d'un TOP 1 (à moins que le table_source choisi ait seulement 1 ligne).
jk7
Merci. Je l'ai changé en PAS EXISTE. Il n'y aura jamais qu'une seule ligne correspondante en raison du test de "clé" selon O / P (bien que cela doive être une clé en plusieurs parties :))
Kristen
4

Cela dépend du modèle d'utilisation. Il faut regarder la situation dans son ensemble sans se perdre dans les détails. Par exemple, si le modèle d'utilisation est de 99% de mises à jour après la création de l'enregistrement, le «UPSERT» est la meilleure solution.

Après la première insertion (hit), ce sera toutes les mises à jour de déclaration unique, pas de si ou de mais. La condition «où» sur l'insert est nécessaire, sinon elle insérera des doublons et vous ne voulez pas vous occuper du verrouillage.

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END
Saleh Najar
la source
2

MS SQL Server 2008 présente la déclaration MERGE, qui, je crois, fait partie de la norme SQL: 2003. Comme beaucoup l'ont montré, ce n'est pas un gros problème de gérer des cas sur une ligne, mais quand il s'agit de grands ensembles de données, il faut un curseur, avec tous les problèmes de performances qui en découlent. La déclaration MERGE sera très appréciée pour les grands ensembles de données.

bjorsig
la source
1
Je n'ai jamais eu besoin d'utiliser un curseur pour le faire avec de grands ensembles de données. Vous avez juste besoin d'une mise à jour qui met à jour les enregistrements qui correspondent et d'une insertion avec une sélection au lieu d'une clause de valeurs qui a laissé des jointures à la table.
HLGEM
1

Avant que tout le monde passe à HOLDLOCK-s par peur de ces utilisateurs nafariés exécutant directement vos sprocs :-) permettez-moi de souligner que vous devez garantir l'unicité des nouveaux PK-s par conception (clés d'identité, générateurs de séquence dans Oracle, index uniques pour ID-s externes, requêtes couvertes par des index). C'est l'alpha et l'oméga du problème. Si vous ne l'avez pas, aucun HOLDLOCK de l'univers ne vous sauvera et si vous l'avez, alors vous n'avez besoin de rien au-delà de UPDLOCK sur la première sélection (ou d'utiliser la mise à jour en premier).

Les Sprocs fonctionnent normalement dans des conditions très contrôlées et avec l'hypothèse d'un appelant de confiance (niveau intermédiaire). Cela signifie que si un modèle simple d'insertion (mise à jour + insertion ou fusion) voit un PK en double, cela signifie un bogue dans votre conception de niveau intermédiaire ou de table et il est bon que SQL crie une faute dans un tel cas et rejette l'enregistrement. Placer un HOLDLOCK dans ce cas équivaut à manger des exceptions et à prendre des données potentiellement défectueuses, en plus de réduire votre perf.

Cela dit, l'utilisation de MERGE ou UPDATE puis INSERT est plus facile sur votre serveur et moins sujette aux erreurs car vous n'avez pas à vous rappeler d'ajouter (UPDLOCK) pour sélectionner d'abord. De plus, si vous effectuez des insertions / mises à jour en petits lots, vous devez connaître vos données afin de décider si une transaction est appropriée ou non. Si c'est juste une collection de documents non liés, une transaction "enveloppante" supplémentaire sera préjudiciable.

ZXX
la source
1
Si vous faites juste une mise à jour puis insérez sans aucun verrouillage ou isolation élevée, alors deux utilisateurs pourraient essayer de renvoyer les mêmes données (je ne considérerais pas cela comme un bogue dans le niveau intermédiaire si deux utilisateurs essayaient de soumettre exactement les mêmes informations à en même temps - dépend beaucoup du contexte, n'est-ce pas?). Ils entrent tous les deux dans la mise à jour, qui renvoie 0 lignes pour les deux, puis essaient tous les deux d'insérer. L'un gagne, l'autre obtient une exception. C'est ce que les gens essaient généralement d'éviter.
Aaron Bertrand
1

Les conditions de course sont-elles vraiment importantes si vous essayez d'abord une mise à jour suivie d'un encart? Disons que vous avez deux threads qui veulent définir une valeur pour la clé clé :

Fil 1: valeur = 1
Fil 2: valeur = 2

Exemple de scénario de conditions de concurrence

  1. la clé n'est pas définie
  2. Le thread 1 échoue avec la mise à jour
  3. Le thread 2 échoue avec la mise à jour
  4. Exactement l'un des threads 1 ou 2 réussit avec l'insertion. Par exemple, le fil 1
  5. L'autre thread échoue avec l'insertion (avec une clé en double d'erreur) - thread 2.

    • Résultat: la "première" des deux marches à insérer, décide de la valeur.
    • Résultat souhaité: le dernier des 2 threads pour écrire des données (mise à jour ou insertion) doit décider de la valeur

Mais; dans un environnement multithread, le planificateur du système d'exploitation décide de l'ordre d'exécution des threads - dans le scénario ci-dessus, où nous avons cette condition de concurrence critique, c'est le système d'exploitation qui a décidé de la séquence d'exécution. C'est à dire: Il est faux de dire que "thread 1" ou "thread 2" était "premier" du point de vue du système.

Lorsque le temps d'exécution est si proche pour le thread 1 et le thread 2, le résultat de la condition de concurrence n'a pas d'importance. La seule exigence doit être que l'un des threads définisse la valeur résultante.

Pour la mise en œuvre: Si la mise à jour suivie de l'insertion entraîne une erreur "clé en double", cela doit être traité comme un succès.

De plus, il ne faut bien sûr jamais supposer que la valeur dans la base de données est la même que la valeur que vous avez écrite en dernier.

runec
la source
1

Dans SQL Server 2008, vous pouvez utiliser l'instruction MERGE

Bart
la source
11
c'est un commentaire. en l'absence de tout exemple de code, c'est comme beaucoup d'autres commentaires sur le site.
swasheck
Très vieux, mais un exemple serait bien.
Matt McCabe
0

J'avais essayé la solution ci-dessous et cela fonctionne pour moi, lorsqu'une demande simultanée d'insertion se produit.

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran
Dev
la source
0

Vous pouvez utiliser cette requête. Fonctionne dans toutes les éditions de SQL Server. C'est simple et clair. Mais vous devez utiliser 2 requêtes. Vous pouvez utiliser si vous ne pouvez pas utiliser MERGE

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

REMARQUE: veuillez expliquer les réponses négatives

Victor Sanchez
la source
Je suppose un manque de verrouillage?
Zeek2
Le verrouillage ne manque pas ... J'utilise "TRAN". Les transactions par défaut du serveur SQL ont un verrouillage.
Victor Sanchez
-2

Si vous utilisez ADO.NET, le DataAdapter gère cela.

Si vous voulez le gérer vous-même, voici la façon:

Assurez-vous qu'il existe une contrainte de clé primaire sur votre colonne de clé.

Alors vous:

  1. Faites la mise à jour
  2. Si la mise à jour échoue car un enregistrement avec la clé existe déjà, effectuez l'insertion. Si la mise à jour n'échoue pas, vous avez terminé.

Vous pouvez également le faire dans l'autre sens, c'est-à-dire faire l'insertion en premier et effectuer la mise à jour si l'insertion échoue. Normalement, la première façon est meilleure, car les mises à jour sont effectuées plus souvent que les insertions.

nruessmann
la source
... et faire l'insertion en premier (sachant qu'elle échouera parfois) coûte cher à SQL Server. sqlperformance.com/2012/08/t-sql-queries/error-handling
Aaron Bertrand
-3

Faire un if existe ... sinon ... implique de faire deux requêtes minimum (une pour vérifier, une pour agir). L'approche suivante nécessite un seul où l'enregistrement existe, deux si une insertion est requise:

DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
  INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')
Luke Bennett
la source
-3

Je fais habituellement ce que plusieurs des autres affiches ont dit en ce qui concerne la vérification de son existence en premier, puis en faisant le bon chemin. Une chose que vous devez retenir lorsque vous effectuez cette opération est que le plan d'exécution mis en cache par sql peut être non optimal pour un chemin ou l'autre. Je crois que la meilleure façon de procéder consiste à appeler deux procédures stockées différentes.

FirstSP:
S'il existe
   Appelez SecondSP (UpdateProc)
Autre
   Appelez ThirdSP (InsertProc)

Maintenant, je ne suis pas très souvent mon propre conseil, alors prenez-le avec un grain de sel.

Micky McQuade
la source
Cela peut être pertinent dans les anciennes versions de SQL Server, mais les versions modernes ont une compilation au niveau des instructions. Les fourches, etc. ne sont pas un problème, et l'utilisation de procédures distinctes pour ces choses ne résout en aucun cas les problèmes inhérents au choix entre une mise à jour et un insert ...
Aaron Bertrand
-10

Faites un choix, si vous obtenez un résultat, mettez-le à jour, sinon, créez-le.

Clint Ecker
la source
3
Cela fait deux appels à la base de données.
Chris Cudmore
3
Je ne vois pas de problème avec ça.
Clint Ecker
10
Ce sont deux appels à la base de données qui sont le problème, vous finissez par doubler le nombre d'aller-retour vers la base de données. Si l'application frappe la base de données avec beaucoup d'inserts / mises à jour, cela nuira aux performances. UPSERT est une meilleure stratégie.
Kev
5
cela crée également une condition de concurrence non?
niico