Edit 1: added one small note regarding ANSI_WARNINGS & ARITHABORT OFF
.
If you use SQL Server 2008 (I see you have questions regarding this version) you could try composable DML.
Simple solution:
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;
Complex scenario (including errors):
1.First test case demonstrates this "technique".
2.The second test demonstrates the behavior when an error is encountered during statement execution: the statement (INSERT + DELETE OUTPUT
) is canceled but the batch is still executed till last statement.
3.For the third test you can see that an error can abort the "entire" batch and the statement (INSERT + DELETE OUTPUT
) is, also, canceled.
The behavior regarding errors is controlled in this script using three settings: ANSI_WARNINGS, ARITHABORT and XACT_ABORT. When both settings (ANSI_WARNINGS and ARITHABORT) are OFF
then this expression 1/0
will be evaluated to NULL
=> so, will be 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;