web-dev-qa-db-fra.com

Comment insérer (mettre à jour ou insérer) dans SQL Server 2005

J'ai une table dans laquelle j'insère des lignes pour l'employé, mais la prochaine fois que je veux insérer une ligne, je ne veux pas réinsérer les données pour cet employé, je veux juste mettre à jour avec les colonnes requises si elle y quitte sinon sinon créer une nouvelle ligne

Comment faire cela dans SQL Server 2005?

J'utilise jsp

ma requête est

String sql="insert into table1(id,name,itemname,itemcatName,itemQty)values('val1','val2','val3','val4','val5')";

si c'est la première fois, insérez-le dans la base de données sinon s'il existe, mettez-le à jour

comment faire?

38
ybc126

Essayez de vérifier l'existence:

IF NOT EXISTS (SELECT * FROM dbo.Employee WHERE ID = @SomeID)

    INSERT INTO dbo.Employee(Col1, ..., ColN)
    VALUES(Val1, .., ValN)

ELSE

    UPDATE dbo.Employee
    SET Col1 = Val1, Col2 = Val2, ...., ColN = ValN
    WHERE ID = @SomeID

Vous pouvez facilement envelopper cela dans une procédure stockée et simplement appeler cette procédure stockée de l'extérieur (par exemple à partir d'un langage de programmation comme C # ou tout ce que vous utilisez).

pdate: soit vous pouvez simplement écrire cette instruction entière dans une longue chaîne (faisable - mais pas vraiment très utile) - ou vous pouvez l'encapsuler dans une procédure stockée:

CREATE PROCEDURE dbo.InsertOrUpdateEmployee
       @ID INT,
       @Name VARCHAR(50),
       @ItemName VARCHAR(50),  
       @ItemCatName VARCHAR(50),
       @ItemQty DECIMAL(15,2)
AS BEGIN
    IF NOT EXISTS (SELECT * FROM dbo.Table1 WHERE ID = @ID)
       INSERT INTO dbo.Table1(ID, Name, ItemName, ItemCatName, ItemQty)
       VALUES(@ID, @Name, @ItemName, @ItemCatName, @ItemQty)
    ELSE
       UPDATE dbo.Table1
       SET Name = @Name,
           ItemName = @ItemName,
           ItemCatName = @ItemCatName,
           ItemQty = @ItemQty
       WHERE ID = @ID
END

puis appelez simplement cette procédure stockée à partir de votre code ADO.NET

55
marc_s

Vous pouvez utiliser @@ROWCOUNT pour vérifier si la ligne doit être insérée ou mise à jour:

update table1 
set name = 'val2', itemname = 'val3', itemcatName = 'val4', itemQty = 'val5'
where id = 'val1'
if @@ROWCOUNT = 0
insert into table1(id, name, itemname, itemcatName, itemQty)
values('val1', 'val2', 'val3', 'val4', 'val5')

dans ce cas, si la mise à jour échoue, la nouvelle ligne sera insérée

27
Evgeny Gorb

Vous pouvez vérifier si la ligne existe, puis INSÉRER ou METTRE À JOUR, mais cela garantit que vous effectuerez deux opérations SQL au lieu d'une:

  1. vérifier si la ligne existe
  2. insérer ou mettre à jour une ligne

Une meilleure solution consiste à toujours METTRE À JOUR en premier, et si aucune ligne n'a été mise à jour, faites un INSERT, comme ceci:

update table1 
set name = 'val2', itemname = 'val3', itemcatName = 'val4', itemQty = 'val5'
where id = 'val1'

if @@ROWCOUNT = 0
  insert into table1(id, name, itemname, itemcatName, itemQty)
  values('val1', 'val2', 'val3', 'val4', 'val5')

Cela prendra soit une opération SQL, soit deux opérations SQL, selon que la ligne existe déjà.

Mais si les performances sont vraiment un problème, vous devez déterminer si les opérations sont plus susceptibles d'être INSERT ou UPDATE. Si les MISES À JOUR sont plus courantes, procédez comme ci-dessus. Si les INSERT sont plus courants, vous pouvez le faire en sens inverse, mais vous devez ajouter la gestion des erreurs.

BEGIN TRY
  insert into table1(id, name, itemname, itemcatName, itemQty)
  values('val1', 'val2', 'val3', 'val4', 'val5')
END TRY
BEGIN CATCH
  update table1 
  set name = 'val2', itemname = 'val3', itemcatName = 'val4', itemQty = 'val5'
  where id = 'val1'
END CATCH

Pour être vraiment certain si vous devez effectuer une MISE À JOUR ou INSÉRER, vous devez effectuer deux opérations au sein d'une même TRANSACTION. Théoriquement, juste après la première UPDATE ou INSERT (ou même la vérification EXISTS), mais avant la prochaine instruction INSERT/UPDATE, la base de données aurait pu changer, entraînant tout de même l'échec de la deuxième instruction. Cela est extrêmement rare et les frais généraux des transactions n'en valent peut-être pas la peine.

Alternativement, vous pouvez utiliser une seule opération SQL appelée MERGE pour effectuer une INSERT ou une UPDATE, mais c'est aussi probablement exagéré pour cette opération d'une ligne.

Pensez à lire instructions de transaction SQL , conditions de concurrence critique , instruction SQL MERGE .

7
Abacus

Voici un article utile de Michael J. Swart sur le sujet, qui couvre différents modèles et antipatterns pour implémenter UPSERT dans SQL Server:
https://michaeljswart.com/2017/07/sql-server-upsert-patterns-and-antipatterns/

Il résout les problèmes de concurrence associés (violations de clé primaire, blocages) - toutes les réponses fournies ici sont encore considérées comme des contre-modèles dans l'article (à l'exception de la solution @Bridge utilisant des déclencheurs, qui n'est pas abordée ici).

Voici un extrait de l'article avec la solution privilégiée par l'auteur:

À l'intérieur d'une transaction sérialisable avec des conseils de verrouillage:

CREATE PROCEDURE s_AccountDetails_Upsert ( @Email nvarchar(4000), @Etc nvarchar(max) )
AS 
  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  BEGIN TRAN

    IF EXISTS ( SELECT * FROM dbo.AccountDetails WITH (UPDLOCK) WHERE Email = @Email )

      UPDATE dbo.AccountDetails
         SET Etc = @Etc
       WHERE Email = @Email;

    ELSE 

      INSERT dbo.AccountDetails ( Email, Etc )
      VALUES ( @Email, @Etc );

  COMMIT

Il y a aussi une question connexe avec des réponses ici sur stackoverflow: Insérer la mise à jour du proc stocké sur SQL Server

3
protasovams

Vous pouvez le faire avec un INSTEAD OF INSERT déclencheur sur la table, qui vérifie l'existence de la ligne puis met à jour/insère selon qu'elle existe déjà. Il existe un exemple de procédure à suivre pour SQL Server 2000+ sur MSDN ici :

CREATE TRIGGER IO_Trig_INS_Employee ON Employee
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
-- Check for duplicate Person. If no duplicate, do an insert.
IF (NOT EXISTS (SELECT P.SSN
      FROM Person P, inserted I
      WHERE P.SSN = I.SSN))
   INSERT INTO Person
      SELECT SSN,Name,Address,Birthdate
      FROM inserted
ELSE
-- Log attempt to insert duplicate Person row in PersonDuplicates table.
   INSERT INTO PersonDuplicates
      SELECT SSN,Name,Address,Birthdate,SUSER_SNAME(),GETDATE()
      FROM inserted
-- Check for duplicate Employee. If no duplicate, do an insert.
IF (NOT EXISTS (SELECT E.SSN
      FROM EmployeeTable E, inserted
      WHERE E.SSN = inserted.SSN))
   INSERT INTO EmployeeTable
      SELECT EmployeeID,SSN, Department, Salary
      FROM inserted
ELSE
--If duplicate, change to UPDATE so that there will not
--be a duplicate key violation error.
   UPDATE EmployeeTable
      SET EmployeeID = I.EmployeeID,
          Department = I.Department,
          Salary = I.Salary
   FROM EmployeeTable E, inserted I
   WHERE E.SSN = I.SSN
END
3
Bridge