Редактировать 1: добавлена одна небольшая заметка относительно ANSI_WARNINGS & ARITHABORT OFF
.
Если вы используете SQL Server 2008 (я вижу, у вас есть вопросы относительно этой версии), вы можете попробовать компонуемый DML .
Простое решение:
INSERT Target
SELECT q.Id, q.Name, q.Type
FROM
(
DELETE Source
OUTPUT deleted.Id, deleted.Name, deleted.Type
WHERE Type = @Type --or another search condition
) q;
Сложный сценарий (включая ошибки):
1.Первый тестовый пример демонстрирует эту «технику».
2. Второй тест демонстрирует поведение при обнаружении ошибки во время выполнения оператора:оператор (INSERT + DELETE OUTPUT
) отменен, но пакет все еще выполняется до последнего оператора.
3.В третьем тесте вы можете видеть, что ошибка может прервать «весь» пакет и оператор (INSERT + DELETE OUTPUT
) также отменяется.
Поведение в отношении ошибок контролируется в этом сценарии с использованием трех настроек: ANSI_WARNINGS, ARITHABORT и XACT_ABORT .Если оба параметра (ANSI_WARNINGS и ARITHABORT) равны OFF
, тогда это выражение 1/0
будет оценено как NULL
=>, поэтому будет INSERT ... NULL.
SET NOCOUNT ON;
CREATE TABLE dbo.Source (Id INT PRIMARY KEY, Name VARCHAR(10) NOT NULL, Type TINYINT NOT NULL);
INSERT dbo.Source (Id, Name, Type) VALUES (1,'A',1), (2, 'B',1), (3, 'C',2), (4, 'D',2), (5, 'E',2);
CREATE TABLE dbo.Target (Id INT PRIMARY KEY, Name VARCHAR(10) NOT NULL, Type TINYINT /*NOT*/ NULL);
--***** Test 1 Ok *****
DECLARE @Type INT = 1;
SELECT 'Test 1 Ok' AS Description;
BEGIN TRAN;
INSERT Target
SELECT q.Id, q.Name, q.Type
FROM
(
DELETE Source
OUTPUT deleted.Id, deleted.Name, deleted.Type
WHERE Type = @Type
) q;
SELECT * FROM Target;
SELECT * FROM Source;
--It will be fine to COMMIT transaction but I will cancel to run the second and third test
ROLLBACK TRAN
SELECT 'End of Test 1 Ok' AS Description;
GO
--***** End of Test 1 *****
--***** Test 2 Err *****
--Start another batch
GO
SET ARITHABORT ON;
SET ANSI_WARNINGS ON;
SET XACT_ABORT OFF;
DECLARE @Type INT = 1;
SELECT 'Test 2 Err' AS Description, SESSIONPROPERTY('ARITHABORT') [ARITHABORT_STATUS], SESSIONPROPERTY('ANSI_WARNINGS') [ANSI_WARNINGS_STATUS];
INSERT Target
--Divide by zero => Abort statement only
SELECT q.Id, q.Name, CASE WHEN q.Id <> 2 THEN q.Type ELSE 1/0 END
FROM
(
DELETE Source
OUTPUT deleted.Id, deleted.Name, deleted.Type
WHERE Type = @Type
) q;
SELECT * FROM Target;
SELECT * FROM Source;
SELECT 'End of Test 2 Err' AS Description;
--***** End of Test 2 *****
--***** Test 3 *****
--Start another batch
GO
SET ANSI_WARNINGS OFF;
SET ARITHABORT ON;
SET XACT_ABORT OFF;
DECLARE @Type INT = 1;
SELECT 'Test 3 Err' AS Description, SESSIONPROPERTY('ARITHABORT') [ARITHABORT_STATUS], SESSIONPROPERTY('ANSI_WARNINGS') [ANSI_WARNINGS_STATUS];
INSERT Target
--Divide by zero => Abort batch
SELECT q.Id, q.Name, CASE WHEN q.Id <> 2 THEN q.Type ELSE 1/0 END
FROM
(
DELETE Source
OUTPUT deleted.Id, deleted.Name, deleted.Type
WHERE Type = @Type
) q
--This statement is not executed
SELECT * , 1 AS Statement FROM Target;
--This statement is not executed
SELECT * , 1 AS Statement FROM Source;
--This statement is not executed
SELECT 'End of Test 3 Err' AS Description
GO --Start another batch
SELECT * , 2 AS Statement FROM Target;
SELECT * , 2 AS Statement FROM Source;
--***** End of Test 3 *****
DROP TABLE dbo.Source;
DROP TABLE dbo.Target;