views:

138

answers:

4

I am trying to write a trigger on a table to avoid insertion of two Names which are not flagged as IsDeleted. But the first part of selection contains the inserted one and so the condition is always true. I though that using FOR keyword causes the trigger to run before the INSERTION but in this case the inserted row is already in the table. Am I wrong or this is how all FOR trigger work?

ALTER TRIGGER TriggerName
ON MyTable
FOR INSERT, UPDATE
AS
BEGIN 
    If exist (select [Name] From MyTable WHERE IsDeleted = 0 AND [Name] in (SELECT [Name] FROM INSERTED)
    BEGIN
    RAISERROR ('ERROR Description', 16, 1);
    Rollback;
    END  
END
+2  A: 

FOR runs after the data is changed, INSTEAD OF is what I think you are after.

EDIT: As stated by others, INSTEAD OF runs instead of the data you are changing, therefore you need to insert the data if it is valid, rather than stopping the insert if it is invalid.

Read this question for a much more detailed explanation of the types of Triggers.

http://stackoverflow.com/questions/405288/sql-server-after-insert-trigger-doesnt-see-the-just-inserted-row

Robin Day
Instead means that the trigger will execute instead of the command you're executing
Frederik Gheysels
+1  A: 

FOR is the same as AFTER. if you want to "simulate" BEFORE trigger, use INSTEAD OF, caveat, it's not exactly what you would expect on proper BEFORE trigger, i.e. if you fail to provide the necessary INSTEAD action, your inserted/updated data could be lost/ignored.

MSSQL doesn't have BEFORE trigger.

Michael Buen
A: 

For SQL Server, FOR runs AFTER the SQL which triggered it.

From: http://msdn.microsoft.com/en-us/library/ms189799.aspx

FOR | AFTER

AFTER specifies that the DML trigger is fired only when all operations specified in the triggering SQL statement have executed successfully. All referential cascade actions and constraint checks also must succeed before this trigger fires.

AFTER is the default when FOR is the only keyword specified.

AFTER triggers cannot be defined on views.

Winston Smith
A: 

I've actually ran into a similar problem lately, and found a cool way to handle it. I had a table which could have several rows for one id, but only ONE of them could be marked as primary.

In SQL Server 2008, you'll be able to make a partial unique index something like this:

create unique index IX on MyTable(name) where isDeleted = 0;

However, you can accomplish it with a little more work in SQL Server 2005. The trick is to make a view showing only the rows which aren't deleted, and then create a unique clustered index on it:

 create view MyTableNotDeleted_vw
 with schema_binding /* Must be schema bound to create an indexed view */
 as
 select name 
 from dbo.MyTable /* Have to use dbo. for schema bound views */
 where isDeleted = 0;

 GO

 create unique clustered index IX on MyTableNotDeleted_vw ( name );

This will effectively create a unique constraint only affecting rows that haven't yet been deleted, and will probably perform better than a custom trigger!

John Gibb