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?
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
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
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:
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 .
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
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