Ошибка вставки строки дубликата ключа при операторе удаления

#sql #sql-server-2014

#sql #sql-сервер-2014 #sql-server-2014

Вопрос:

Я выполняю следующую DELETE инструкцию в SQL, но получаю приведенное ниже сообщение об ошибке.

 DELETE PSRO
FROM ROLEUSER PSRO
INNER JOIN PS_GH_AD_X_WALK B ON B.OPRID = PSRO.ROLEUSER
INNER JOIN HSDEV185.HSDEV92B.dbo.PS_JOB C ON C.EMPLID = B.GH_AD_EMPLID AND B.GH_AD_EMPLID <> ''
WHERE C.EFFDT = 
(SELECT MAX(A_ED.EFFDT) FROM HSDEV185.HSDEV92B.dbo.PS_JOB A_ED 
WHERE C.EMPLID = A_ED.EMPLID 
AND A_ED.EFFDT <= SUBSTRING(CONVERT(CHAR,GETDATE(),121), 1, 10)) 


Msg 2601, Level 14, State 1, Procedure ROLEUSER_TR, Line 67
Cannot insert duplicate key row in object 'dbo.AUDIT_ROLEUSR' with unique index 'AUDIT_ROLEUSR'. The duplicate key value is (Native SQL                    , Apr 23 2019  1:15PM, D).
The statement has been terminated.
  

Я в замешательстве, почему SQL интерпретирует мою DELETE команду как insert?

РЕДАКТИРОВАТЬ: Существует следующий триггер, связанный с таблицей ROLEUSER. Я не очень знаком с триггерами. Есть ли способ предотвратить вставку, когда это удаление выполняется через SQL (в отличие от удаления, происходящего онлайн в системе, для чего и был предназначен триггер)?

 ALTER TRIGGER [dbo].[PSROLEUSER_TR] ON [dbo].[ROLEUSER]
FOR INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON
DECLARE @XTYPE CHAR(1), @OPRID CHAR(30)
SET @OPRID = NULL
SELECT @OPRID = case (charindex(',',
cast(context_info as char(128))))
when 0 then 'Native SQL'
else 
substring(cast(context_info as
CHAR(128)),1,(charindex(',',cast(context_info as char(128)))-1))
end
FROM sys.sysprocesses
WHERE spid = @@spid
-- Determine Transaction Type
IF EXISTS (SELECT * FROM DELETED)
BEGIN
SET @XTYPE = 'D'
END

IF EXISTS (SELECT * FROM INSERTED)
BEGIN
IF (@XTYPE = 'D')
 BEGIN
  SET @XTYPE = 'U'
 END
ELSE
 BEGIN
  SET @XTYPE = 'I'
 END
END
-- Transaction is a Delete
IF (@XTYPE = 'D')
BEGIN
INSERT INTO PS_AUDIT_PSROLEUSR
(AUDIT_OPRID,AUDIT_STAMP,AUDIT_ACTN,
ROLEUSER,
ROLENAME,
DYNAMIC_SW)
SELECT @OPRID,getdate(),'D',
ROLEUSER,
ROLENAME,
DYNAMIC_SW FROM deleted 
END
-- Transaction is a Insert
IF (@XTYPE = 'I')
BEGIN
INSERT INTO PS_AUDIT_PSROLEUSR
(AUDIT_OPRID,AUDIT_STAMP,AUDIT_ACTN,
ROLEUSER,
ROLENAME,
DYNAMIC_SW)
SELECT @OPRID,getdate(),'A',
ROLEUSER,
ROLENAME,
DYNAMIC_SW FROM inserted 
END
-- Transaction is a Update
IF (@XTYPE = 'U')
BEGIN
-- Before Update
INSERT INTO PS_AUDIT_PSROLEUSR
(AUDIT_OPRID,AUDIT_STAMP,AUDIT_ACTN,
ROLEUSER,
ROLENAME,
DYNAMIC_SW)
SELECT @OPRID,getdate(),'K',
ROLEUSER,
ROLENAME,
DYNAMIC_SW FROM deleted 
-- After Update
INSERT INTO PS_AUDIT_PSROLEUSR
(AUDIT_OPRID,AUDIT_STAMP,AUDIT_ACTN,
ROLEUSER,
ROLENAME,
DYNAMIC_SW)
SELECT @OPRID,getdate(),'N',
ROLEUSER,
ROLENAME,
DYNAMIC_SW FROM inserted 
END
  

Комментарии:

1. Есть ли какие-либо триггеры в ваших таблицах?

2. КАЖЕТСЯ, вы поделились частью запроса, вызывающего dbo. AUDIT_ROLEUSR эта таблица недоступна в этом запросе

3. @rad Да, в таблице есть триггер. Я отредактировал сообщение выше. Не уверен, могу ли я изменить это, чтобы разрешить удаление через SQL без последующей вставки?

4. Это должен быть другой триггер с именем ROLEUSER_TR1 .

5. Будет ли это ситуация, когда использование курсора помогло бы обработать каждую строку по отдельности?

Ответ №1:

Есть ли способ предотвратить вставку при выполнении этого удаления через SQL (в отличие от удаления, происходящего онлайн в системе

Не напрямую, нет. Но вы можете закодировать триггер так, чтобы он не предпринимал никаких действий в некоторых обстоятельствах. Например, если вы добавите это в начало триггера:

 if user_id() = 1 return;
  

Она завершится немедленно, когда DBO (владелец базы данных или системный администратор) выполнит команду, но запустит тело триггера для кого-либо еще.

Комментарии:

1. Спасибо за отзыв. Будет ли это ситуация, когда использование курсора поможет обработать каждую строку по отдельности?

Ответ №2:

Используйте что-то вроде

 alter index AUDIT_ROLEUSR on TableName
    rebuild with (ignore_dup_key = on)
  

или проверьте свою процедуру ROLEUSER_TR в строке 67 и убедитесь, что триггеров нет.

Редактировать:

Если вы хотите выполнять это удаление регулярно, вы можете создать процедуру, которая отключит индекс, выполнит удаление и снова включит его.