Summary
I just discovered a surprising behavior of SQL Server: if a statement is executed outside of a TRY block, and it violates a constraint1, the execution will continue to the next statement.
I would have expected the execution to stop, like for an exception, but apparently constraint violations do not (always) behave like exceptions. However, if a statement is executed inside a TRY block, a constraint violation behaves like exception and stops the flow of execution before it reaches the next statement.
Test
Here is my test setup...
CREATE TABLE T (
T_ID int,
CONSTRAINT T_PK PRIMARY KEY (T_ID)
);
INSERT INTO T VALUES (1);
Below, the INSERT has violated the primary key, but the next statement is executed nonetheless ('next reached' is printed):
INSERT INTO T VALUES(1);
PRINT 'next reached';
Msg 2627, Level 14, State 1, Line 10
Violation of PRIMARY KEY constraint 'T_PK'. Cannot insert duplicate key in object 'dbo.T'. The duplicate key value is (1).
The statement has been terminated.
next reached
This is different from the "normal" exception ('next reached' is not printed):
THROW 50000, 'Some exception.', 0;
PRINT 'next reached';
Msg 50000, Level 16, State 0, Line 15
Some exception.
Curiously, if I put the INSERT into a TRY block, the constraint violation starts behaving like an exception (note how neither 'next reached' nor 'end reached' are printed):
BEGIN TRY
INSERT INTO T VALUES(1);
PRINT 'next reached';
END TRY
BEGIN CATCH
THROW;
END CATCH
PRINT 'end reached'
(0 rows affected)
Msg 2627, Level 14, State 1, Line 21
Violation of PRIMARY KEY constraint 'T_PK'. Cannot insert duplicate key in object 'dbo.T'. The duplicate key value is (1).
Question
Is there a way to make the constraint violations behave like exceptions even when not enclosed within a TRY block?
Motivation: The above behavior is different from all programming languages supporting exceptions that I know of. When implementing complex business logic in stored procedures, using TRY everywhere would make the code less readable. More importantly, it would make it fragile: forgetting it somewhere, or relying on a caller to have it, and then somebody independently removing it from the caller, could easily make the stored procedure continue execution unexpectedly and potentially even corrupt the data.
1 I have tested PRIMARY KEY, UNIQUE, FOREIGN KEY and CHECK constraints, and they all seem to behave the same.
--- EDIT ---
For what it's worth, I have created a suggestion for Microsoft, but I'm not holding my breath that it will be implemented soon (or ever):
Consistent flow of execution with arithmetic errors and constraint violations