views:

251

answers:

3

Is there anyway where I can create a trigger which will execute before the update/delete takes place( and then the actual update/delete takes place)? and how can I drop a trigger from a table?

A: 

to drop trigger- use database_name

IF EXISTS (SELECT name FROM sysobjects
  WHERE name = 'tgr_name' AND type = 'TR')    
DROP TRIGGER tgr_name
GO
nectar
+1  A: 

This article from microsoft explains the syntax of creating triggers.

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

There isn't really a 'before' trigger, but you can use an INSTEAD OF trigger that allows you to jump in place of whatever action is attempted, then define your own action.

I've used that technique for versioning data.

CREATE TRIGGER [dbo].[Documents_CreateVersion] 
   ON  [dbo].[Documents] 
   INSTEAD OF UPDATE

AS 
BEGIN

  DECLARE @DocumentID int
  SELECT DocumentID  = DocumentID FROM INSERTED

  -- do something

END

INSERTED is a bit of a misnomer here, but it contains the details of the action before it occurs you can then define your own action with that data.

Edit:

As per comments below my response, my example can be dangerous if multiple rows are updated at once. My application doesn't allow for this so it's fine in this case. I would agree that the above is a bad practice regardless.

ScottE
@ScottE suppose I have to check `childcount` of a perticular user if that is more than `5` do not update the user.how can I do that using instead of trigger?
nectar
never code your triggers this way! you assume only one row will be in the INSERTED table, this will fail if multiple rows are there.
KM
Good point KM. My particular example is for a table that will never have multiple updates happening. This is a bad example! Thanks.
ScottE
even if your application will only affect one row, it is best to always write your triggers to handle multiple rows. if you run some one time script on a table that affects all rows, then your (assume one row) trigger will fail. what if you run a manual update on multiple rows, etc? what if the application changes? it is always best to write a trigger to accept multiple rows. if you don't want to, add code into the trigger to `COUNT(*) FROM INSERTED` or `COUNT(*) FROM DELETED` and fail if `>1`. to make sure your "only ever one" is enforced by the trigger that is written to only handle one row.
KM
In my application an update of that nature would just fail as it generates a subquery that returns more than one value, so no need for the check. Point taken, however.
ScottE
+1  A: 

to drop a trigger use:

--SQL Server 2005+, drop the trigger, no error message if it does not exist yet
BEGIN TRY DROP TRIGGER dbo.TrigerYourTable END TRY BEGIN CATCH END CATCH
GO

--drop trigger pre-SQl Server 2005, no error message if it does not exist yet
if exists (select * from sysobjects where id = object_id(N'[dbo].[TrigerYourTable ]') and OBJECTPROPERTY(id, N'IsTrigger') = 1)
    drop trigger [dbo].[TrigerYourTable ]
GO

OP said in a comment:

...suppose I have to check childcount of a perticular user if that is more than 5 do not update the user.how can I do that using instead of trigger?

You don't really need to prevent the original update, you can let it happen, and then in the trigger check for the problem and rollback if necessary. This is how to enforce the logic for one or many affected rows, when you need to JOIN to determine the childcount of the affected rows:

--create the trigger
CREATE TRIGGER dbo.TrigerYourTable ON dbo.YourTable
   AFTER UPDATE
AS 
SET NOCOUNT ON

IF EXISTS (SELECT
               1
               FROM INSERTED                      i
                   INNER JOIN YourChildrenTable   c ON i.ParentID=c.ParentID
               GROUP BY i.ParentID
               HAVING COUNT(i.ParentID)>5
          )
BEGIN
    RAISERROR('Count of children can not exceed 5',16,1)
    ROLLBACK
    RETURN
END

GO

It will throw the error if there is a violation of the logic, and the original command will be subject to a rollback.

If childcount is a column within the affected table, then use a trigger like this to enforce the logic:

--create the trigger
CREATE TRIGGER dbo.TrigerYourTable ON dbo.YourTable
   AFTER UPDATE
AS 
SET NOCOUNT ON

IF EXISTS (SELECT 1 FROM INSERTED WHERE childcount>5)
BEGIN
    RAISERROR('Count of children can not exceed 5',16,1)
    ROLLBACK
    RETURN
END

GO

If you just want to ignore the update for any rows that violate the rule try this:

--create the trigger
CREATE TRIGGER dbo.TrigerYourTable ON dbo.YourTable
   INSTEAD OF UPDATE
AS 
SET NOCOUNT ON

UPDATE y
    SET col1=i.col1
       ,col2=i.col2
       ,col3=i.col3
       ,....  --list all columns except the PK column!
    FROM dbo.YourTable       y
        INNER JOIN INSERTED  i on y.PK=i.PK
    WHERE i.childcount<=5
GO

It will only update rows that have a child count less than 5, ignoring all affected rows that fail the requirement (no error message).

KM