在删除 SQL Server 之前执行插入语句

时间:2021-05-28 12:10:47

标签: sql-server tsql stored-procedures transactions

我的存储过程中有如下语句:

IF (  
    SELECT COUNT(1)  
    FROM dbo.t_user_suggested
    WHERE ListId = @pListId  
    ) > 0  
  BEGIN  
   DELETE  
   FROM dbo.t_user_suggested
   WHERE ListId = @pListId  
  END

 INSERT INTO dbo.t_user_suggested (  
   Id  
   ,InId  
   ,InName  
   ,UserId  
   ,ListId
   )  
  SELECT DISTINCT Id  
   ,InId  
   ,InName  
   ,@pUserId  
   ,@pListId
  FROM CTE123  

我收到错误违反 PRIMARY KEY 约束“PK_t_user_suggested”。无法在对象“dbo.t_user_suggested”中插入重复键。在插入之前,我有一个明确的检查以删除所有记录,并且此错误是随机出现的。我手动执行了 10-15 次存储过程,没有出现错误。在我的存储过程中控制移动到插入之前,如何确保删除所有记录。

3 个答案:

答案 0 :(得分:2)

如果两个事务同时运行此代码,它们都可以尝试插入相同的密钥。在默认的锁定/隔离级别下,没有什么可以阻止它们,而且您似乎并没有使用事务。

最好的办法是使用带有正确提示的交易。

<块引用>

顺便说一下,不需要 IF (SELECT COUNT(1)...,因为 DELETE 只会删除存在的行。另外,如果你真的需要这个,那么你应该使用 IF(EXISTS 代替

SET XACT_ABORT, NOCOUNT ON;

BEGIN TRAN;

DELETE
FROM dbo.t_user_suggested WITH (HOLDLOCK)
WHERE ListId = @pListId;
 
INSERT INTO dbo.t_user_suggested (  
   Id  
   ,InId  
   ,InName  
   ,UserId  
   ,ListId
   )  
  SELECT DISTINCT Id  
   ,InId  
   ,InName  
   ,@pUserId  
   ,@pListId
  FROM CTE123;

COMMIT TRAN;

或者,如果 @pListId 实际上是主键,那么发出一个 UPDATE 会更好

UPDATE 
SET Id = CTE123.InId
   ,InId  = CTE123.InId  
   ,InName = CTE123.InName 
   ,UserId = CTE123.UserId 
FROM dbo.t_user_suggested t
JOIN (
  SELECT DISTINCT Id  
   ,InId  
   ,InName  
   ,@pUserId AS pUserId
   ,@pListId AS pListId
  FROM CTE123
) CTE123 ON CTE123.pListId = t.pListId
WHERE t.ListId = @pListId;

答案 1 :(得分:0)

我怀疑下面的查询返回了不止一行。因此,您会为每一行获得相同的 ListID。

SELECT DISTINCT Id  
   ,InId  
   ,InName  
   ,@pUserId  
   ,@pListId
  FROM CTE123  

请尝试执行以下查询:

IF (  
    SELECT COUNT(1)  
    FROM dbo.t_user_suggested
    WHERE ListId = @pListId  
    ) > 0  
  BEGIN  
   DELETE  
   FROM dbo.t_user_suggested
   WHERE ListId = @pListId  
  END

 INSERT INTO dbo.t_user_suggested (  
   Id  
   ,InId  
   ,InName  
   ,UserId  
   ,ListId
   )  
  SELECT top 1 DISTINCT Id  
   ,InId  
   ,InName  
   ,@pUserId  
   ,@pListId
  FROM CTE123  

我已尝试创建您的情况,并且您的查询工作正常。您可以查看以下示例。 ListId 不是您的主键列,或者您的选择查询返回多行。

DB-小提琴:

表定义和插入语句:

 create table t_user_suggested (Id  int ,InId  int,InName  varchar(50),UserId  INT,ListId INT);
 INSERT INTO t_user_suggested values(1,1,'A',2,3);

选择查询:

 select* from t_user_suggested

表的当前输出:

<头>
Id InId InName UserId ListId
1 1 A 2 3

您的查询:

删除部分

 IF (  
     SELECT COUNT(1)  
     FROM dbo.t_user_suggested
     WHERE ListId = 3
     )  0  
   BEGIN  
    DELETE  
    FROM dbo.t_user_suggested
    WHERE ListId = 3  
   END

删除后选择查询:

 select * from t_user_suggested;

输出:

<头>
Id InId InName UserId ListId

已成功删除行。

插入语句:

  INSERT INTO dbo.t_user_suggested (  
    Id  
    ,InId  
    ,InName  
    ,UserId  
    ,ListId
    )  
   values(1,1,'A',2,3);  

插入后选择查询:

 select * from t_user_suggested;

输出:

<头>
Id InId InName UserId ListId
1 1 A 2 3

dbhere

答案 2 :(得分:0)

我不确定到底是什么问题,但似乎是竞争条件或事务锁定/隔离级别的问题。我暂时使用了 MERGE 语句而不是插入和删除来解决问题。所以下面是我的查询。

MERGE t_user_suggested AS tar
USING CTE123  AS cte
    ON tar.Id = cte.Id 
        AND tar.InId  = cte.InId  
        AND tar.ListId = @pListId
WHEN NOT MATCHED BY Target
    THEN
        INSERT (
            Id 
            ,InId  
            ,InName
            ,UserId
            ,ListId
            )
        VALUES (
            cte.Id
            ,cte.IngrId
            ,cte.InName
            ,@pUserId
            ,@pListId
            )
     -- For Updates
    WHEN MATCHED THEN UPDATE SET
        tar.IName       = cte.InName,
        tar.datetimelastseen = null,
        tar.seenlast24hours=null
        -- For Deletes
    WHEN NOT MATCHED BY SOURCE THEN
        DELETE;
相关问题