63

我有一个表,我在其中为员工插入行,但是下次当我想插入行时,我不想再次为该员工插入数据,只想用所需的列进行更新,如果它退出,则创建新行

我们如何在 SQL Server 2005 中做到这一点?

我正在使用jsp

我的查询是

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

如果是第一次,则将其插入数据库,否则如果存在则更新它

怎么做?

4

5 回答 5

81

尝试检查是否存在:

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

您可以轻松地将其包装到一个存储过程中,然后从外部调用该存储过程(例如,从 C# 之类的编程语言或您正在使用的任何语言)。

更新:您可以将整个语句写在一个长字符串中(可行-但不是很有用)-或者您可以将其包装到存储过程中:

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

然后只需从您的 ADO.NET 代码中调用该存储过程

于 2012-06-13T08:02:14.087 回答
68

您可以使用@@ROWCOUNT检查是否应插入或更新行:

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')

在这种情况下,如果更新失败,将插入新行

于 2013-03-13T21:25:35.867 回答
17

您可以检查该行是否存在,然后插入或更新,但这保证您将执行两个 SQL 操作,而不是一个:

  1. 检查行是否存在
  2. 插入或更新行

更好的解决方案是始终先更新,如果没有更新任何行,则执行 INSERT,如下所示:

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')

这将需要一个 SQL 操作或两个 SQL 操作,具体取决于该行是否已存在。

但是,如果性能确实是一个问题,那么您需要确定这些操作是否更有可能是 INSERT 或 UPDATE。如果更新更常见,请执行上述操作。如果 INSERT 更常见,您可以反过来执行此操作,但您必须添加错误处理。

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

要真正确定是否需要执行 UPDATE 或 INSERT,您必须在单个 TRANSACTION 中执行两个操作。理论上,在第一个 UPDATE 或 INSERT(甚至是 EXISTS 检查)之后,但在下一个 INSERT/UPDATE 语句之前,数据库可能已经更改,导致第二个语句无论如何都会失败。这是极其罕见的,交易的开销可能不值得。

或者,您可以使用称为 MERGE 的单个 SQL 操作来执行 INSERT 或 UPDATE,但这对于这一单行操作来说也可能是多余的。

考虑阅读有关SQL 事务语句竞争条件SQL MERGE 语句的内容。

于 2015-01-16T17:49:57.920 回答
13

这是 Michael J. Swart 关于此事的一篇有用文章,其中涵盖了UPSERT在 SQL Server 中实现的不同模式和反模式:
https ://michaeljswart.com/2017/07/sql-server-upsert-patterns-and-antipatterns/

它解决了相关的并发问题(主键违规、死锁)——此处提供的所有答案都被视为文章中的反模式(使用触发器的@Bridge 解决方案除外,此处未涉及)。

以下是文章的摘录,其中包含作者首选的解决方案:

在带有锁提示的可序列化事务中:

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

在stackoverflow上也有相关问题的答案:Insert Update stored proc on SQL Server

于 2019-04-24T14:08:22.390 回答
3

您可以使用INSTEAD OF INSERT表上的触发器来执行此操作,该触发器检查行的存在,然后根据它是否已经存在进行更新/插入。在 MSDN 上有一个如何为 SQL Server 2000+ 执行此操作的示例

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
于 2012-06-13T08:21:44.873 回答