In order for that to work as you've coded it above, you need a rollback after your RAISERROR
or, as the comments correctly state, wrap in a TRY/CATCH.
By itself, it's not going to be treated the same as an actual statement or batch terminating error. For example, this will work the way you expect:
create table T1 ( i1 int );
create table t2 (i2 int primary key);
go
create trigger T1_ForDelete on T1
for delete
as
insert into t2 (i2)
SELECT i1
FROM DELETED
go
insert into T1 (i1) values (1);
insert into t2 (i2) values (1);
delete from T1;
go
SELECT * from t1
In the above code the trigger will error, and cause the delete (outer) transaction to rollback. By itself, RAISERROR is only really transferring control and/or sending messages up the stack (so to speak)
BOL describes this behavior:
When RAISERROR is run with a severity of 11 or higher in a TRY block,
it transfers control to the associated CATCH block. The error is
returned to the caller if RAISERROR is run:
Outside the scope of any TRY block.
With a severity of 10 or lower in a TRY block.
With a severity of 20 or higher that terminates the database
connection.
So RAISERROR outside the scope of a TRY block simply returns the error to the caller and is not treated as a statement-terminating error regardless of the severity you define.
Best Answer
You have two options:
1) Rollback the DELETE in case you're deleting the row "Peter". You do this with an AFTER trigger. Be aware that it will rollback the whole transaction in case yuo're deleting multiple rows among which at least one is "Peter".
2) You override the way SQL Server does DELETEs on the table, using an INSTEAD OF trigger. In this case, you silently ignore DELETEs for the row "Peter".
Which one is best for you highly depends on your business logic.