Creating an INSERT TRIGGER that updates CreatedBy and UpdatedBy without firing the UPDATE TRIGGER on same table - sql

I am trying to create an INSERT TRIGGER and UPDATE TRIGGER on all tables in my database that manage CreatedBy and UpdatedBy fields.
The problem that I am running into is that the following is in my INSERT trigger:
UPDATE MyTable
SET CreatedBy = INSERTED.CreatedBy,
InsertDate = GetDate(),
UpdatedBy = INSERTED.UpdatedBy,
UpdateDate = GetDate()
FROM INSERTED
WHERE MyTable.Id = INSERTED.Id
The problem is that this UPDATE statement is additionally triggering the UPDATE trigger. I can not have this happen. How can I prevent this?

I found a built in SQL function called IF TRIGGER_NESTLEVEL().
I was able to wrap the update trigger with the following to prevent the update trigger from running when the insert trigger runs:
IF TRIGGER_NESTLEVEL() < 2
--Trigger script here
END

Related

Deleted Virtual table in SQL Server is empty ON Delete trigger

I am trying to invoke an ON Delete trigger event, but when I try to select row from the Deleted table, it is empty.
Is there any other way to get Id of row on which delete event has fired?
Code:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER TRIGGER [audit]
ON [dbo].[S_PARTY]
FOR DELETE
AS
-- Insert statements for trigger here
DECLARE #id nvarchar(50)
SET #id = (Select ROW_ID from deleted )
BEGIN
-- Insert statements for trigger here
INSERT INTO [dbo].[S_AMF_AUDIT_ITEM] (ROW_ID)
VALUES (#id);
END
When trigger fires on this, I am getting error as S_AMF_AUDIT_ITEM doesn't allow null values. So can you please help me this to get Id of table on which delete command executes?
Your trigger is broken.
It doesn't take into account that the DELETE statement might affect zero or more than one rows.
The issue with NULL could occur for the statement DELETE FROM [dbo].[S_PARTY] WHERE 1 = 0.
A fixed version would be
ALTER TRIGGER [audit]
ON [dbo].[S_PARTY]
FOR DELETE
AS
BEGIN
INSERT INTO [dbo].[S_AMF_AUDIT_ITEM]
(ROW_ID)
SELECT ROW_ID
FROM deleted;
END
A trigger can fire for 0..N deleted rows. For example:
delete YourTable
where 1=2
Would run a for delete trigger on YourTable. So a trigger has to be able to deal with zero or multiple rows. Consider rewriting your trigger like:
ALTER TRIGGER [audit] ON [dbo].[S_PARTY] FOR DELETE
AS
INSERT dbo.S_AMF_AUDIT_ITEM
(ROW_ID)
SELECT ROW_ID
FROM deleted
That will work for any number of inserted rows.

SQL Server 2008 created/last updated trigger issue

I am using this trigger in order to fill in the CreatedDate and LastUpdated columns (of type datetime) in a table:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
UPDATE [LasMTest]
SET [LasMTest].Created = getdate(),
[LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID]
GO
When I check the table, the dates are off by just a fraction of a second.
Created LastModified ID
2013-03-19 09:24:32.920 2013-03-19 09:24:32.930 4
2013-03-19 09:26:39.890 2013-03-19 09:26:39.900 5
How can I modify the trigger so that they are both the exact time?
It's the interaction of your two triggers that's causing the problem.
If, instead, you set both columns to default to getdate() and ditch your insert trigger, it should work - the INSERT won't also cause an UPDATE.
The alternative is to author your INSERT trigger as an INSTEAD OF trigger that performs an INSERT rather than an UPDATE (and thus, also, avoids the UPDATE trigger firing).
If you do want to write it as an INSTEAD OF trigger, it would be something like:
CREATE TRIGGER trCreatedDate ON [LasMTest]
INSTEAD OF INSERT
AS
INSERT INTO LasMTest (/* Column List */,Created,LastModified)
SELECT /* Column List */,getdate(),getdate() from inserted
GO
INSERTs into the triggering table in an INSTEAD OF trigger don't (thankfully) cause the trigger to be fired recursively. If ID is an IDENTITY column, then it should appear in the column lists above (it hasn't been generated yet).
Try this:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
Declare #CurrentDate Datetime
Set #CurrentDate=getdate()
UPDATE [LasMTest] SET [LasMTest].Created=#CurrentDate,[LasMTest].LastModified=#CurrentDate
FROM [LasMTest] INNER JOIN Inserted ON [LasMTest].[ID]= Inserted.[ID]
GO
You basically want to avoid the UPDATE trigger when doing the update from INSERT. This is called Nested Triggers. One easy solution is to use CONTEXT_INFO() to communicate to the nested UPDATE trigger code that you are already in an INSERT trigger so it suppresses itself:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
SET CONTEXT_INFO 0xDEADBEEF;
UPDATE [LasMTest]
SET [LasMTest].Created = getdate(),
[LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
SET CONTEXT_INFO NULL;
GO
CREATE TRIGGER trModifiedDate ON [LasMTest]
FOR UPDATE
AS
DECLARE #context varbinary(128);
SET #context = CONTEXT_INFO();
IF #context is NULL
BEGIN
UPDATE [LasMTest]
SET [LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
END
GO
However such an approach is fragile. An exception can leave the context_info set and suppress all subsequent UPDATE triggers in the session. This requires adding TRY/CATCH blocks. And you always run the risk of an application using CONTEXT_INFO for its own purposes and ruining your scheme.
Another solution is to make the UPDATE trigger smart. It can check the UPDATE(Created) inside the UPDATE trigger and suppress any action if the Created column was modified. This works by convention, because you know that the only place that updates the Created column is the INSERT trigger:
CREATE TRIGGER trModifiedDate ON [LasMTest]
FOR UPDATE
AS
IF NOT UPDATE(Created)
BEGIN
UPDATE [LasMTest]
SET [LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
END
GO

Trigger being called on all rows in database

I have written a trigger that I want to use for adding the date to a column in a record so that I can keep track of the insert of the item.
There are a large amount of inserts being called (about 20000) and I have noticed that the trigger will update all of the InsertDate columns associated with each item every time a new item is added. How can I make sure this happens to an item being inserted only one time.
My trigger is as follows:
SET ANSI_NULLS ON
SET QUOTED_INDENTIFIER ON
GO
CREATE TRIGGER [InsertDate_Item]
ON [dbo].[ItemHolder]
AFTER INSERT
NOT FOR REPLICATION
AS
UPDATE ItemHolder SET InsertDate = GETDATE()
Any help will be much appreciated.
Thanks
You need to restrict rows to those inserted... using the virtual trigger table INSERTED
CREATE TRIGGER [InsertDate_Item]
ON [dbo].[ItemHolder]
AFTER INSERT
NOT FOR REPLICATION
AS
SET NOCOUNT ON
UPDATE IH
SET InsertDate = GETDATE()
FROM
ItemHolder IH
JOIN
INSERTED INS ON IH.keycol = INS.keycol
Go
One thing: You'd be better adding a default to the table instead. No need for a trigger
ALTER TABLE ItemHolder ADD
CONSTRAINT DF_ItemHolder_InsertDate DEFAULT (GETDATE()) FOR InsertDate
If you are inserting records into a table, why don't you make the InsertDate field have a default value of GetDate()? That avoids the trigger altogether.
I'd go about what you're trying to do without a trigger. Just set the default value of the column to GetDate().
update ih
set ih.insertdate = GetDate()
from itemholder ih inner join inserted i
on ih.itemholderid = i.itemholderid
change:
UPDATE ItemHolder SET InsertDate = GETDATE()
to:
UPDATE ItemHolder SET InsertDate = GETDATE() WHERE InsertDate IS NULL
The above will only set InsertDate if it is null and you still want to use the trigger. Of course, this is assuming the default value of InsertDate is null. If this is not the case let me know.

sql - Update Trigger to populate a ModifyDate field

I'm looking for an example of a good update trigger to update the ModifyDate field of a table.
It would be nice to handle the case where an update command updated more than one record in the table.
Is there a good template or tutorial for this?
Here's a cut and paste (and rename, to protect the innocent) of one I wrote quite some time ago (aka it works):
CREATE TRIGGER dbo.TR_iu_MyTable__LastUpdated
on dbo.MyTable
after insert, update
AS
SET NOCOUNT on
UPDATE dbo.MyTable
set LastUpdated = getdate()
where MyTableId in (select MyTableId from inserted)
UPDATE {tablename}
SET ModifyDate = GETDATE()
FROM inserted
WHERE {tablename}.{primarykey} = inserted.{primarykey}
Placed in a trigger tagged for INSERT and UPDATE actions will address your problem.
You could also do something similar for a CreateDate
UPDATE {tablename}
SET CreateDate = GETDATE()
FROM inserted
WHERE {tablename}.{primarykey} = inserted.{primarykey}
placed in a trigger tagged for INSERT action only.

How to update a single table using trigger in MS SQL 2008

I have a table PeroidicDeduction and the fields are ID(auto-increment),TotalDeduction(e.g.it can be loan),Paid(on which the deduction for each month),RemainingAmount,
What I want is when every time I insert or update the table---RemainingAmount will get the value of TotalDeduction-SUM(Paid)....and writ the following trigger...but dosen't work for me
CREATE TRIGGER dbo.UpdatePD
ON PeroidicDedcution
AFTER INSERT,UPDATE
AS
BEGIN
UPDATE PeroidicDedcution
SET REmaininAmoubnt=(SELECT TotalDeduction-(SELECT SUM(Paid) FROM PeroidicDeduction) FROM PeroidicDeduction)
END
NOTE: it is on a Single table
Create two triggers, an INSTEAD OF UPDATE and INSTEAD OF INSERT. Here is the code for the INSTEAD OF UPDATE:
CREATE TRIGGER dbo.UpdatePD ON PeroidicDedcution
INSTEAD OF Update
AS
SET NOCOUNT ON
UPDATE p
SET col1=i.col1
,col2=i.col2
FROM INSERTED i
INNER JOIN PeroidicDedcution p ON i.PK=p.PK
UPDATE PeroidicDedcution
SET REmaininAmoubnt=(SELECT TotalDeduction-(SELECT SUM(Paid) FROM PeroidicDeduction) FROM PeroidicDeduction)
go
It will do both the original update that fires the trigger, as well as the SUM logic from the trigger in the question.
here is the INSTEAD OF INSERT trigger:
CREATE TRIGGER dbo.InsertPD ON PeroidicDedcution
INSTEAD OF INSERT
AS
SET NOCOUNT ON
INSERT INTO PeroidicDedcution
(col1, col2, ...)
SELECT
col1, col2, ...
FROM INSERTED
UPDATE PeroidicDedcution
SET REmaininAmoubnt=(SELECT TotalDeduction-(SELECT SUM(Paid) FROM PeroidicDeduction) FROM PeroidicDeduction)
go
You should not use a trigger to do this as it will recursively fire itself if you have an update trigger on a table and the trigger causes and update to the same table. rather add the logic to your insert and update stored procedures