MS SQL Trigger for Creation&Modification - sql

I have found a trigger example for Creation and Modification of the record but the question is, should I create those two triggers for each table or is there any way to run them on each update and insert regardless of the table name. Of course the names of the fields will be unique for each table for instance "CreationDate", "LastUpdate". Actually first question should have been, is creating a trigger for such a case a correct practice or should I handle it on code behind?
Here is the trigger that I have found on the internet;
CREATE TRIGGER tr[TableName]CreateDate ON [TableName]
FOR INSERT
AS
UPDATE [TableName] SET [TableName].Created=getdate()
FROM [TableName] INNER JOIN Inserted ON [TableName].[UniqueID]= Inserted.[UniqueID]
GO
CREATE TRIGGER tr[TableName]LastModifiedDate ON [TableName]
FOR UPDATE
AS
UPDATE [TableName] SET [TableName].LastModified=getdate()
FROM [TableName] INNER JOIN Inserted ON [TableName].[UniqueID]= Inserted.[UniqueID]

Triggers can be created on DML (Tables, Views events) or DDL (Create, Alter, Drop etc). You can not create a generic trigger which applies to all tables, you need to specify the table name.
You could create a script which automates the Trigger scripts creation for all tables if need be.
More info on: http://msdn.microsoft.com/en-us/library/ms189799.aspx

Just give your trigger the option to run for INSERT AND UPDATE
CREATE TRIGGER [dbo].[tr_TableName] ON [dbo].[TableName] FOR INSERT,DELETE,UPDATE AS
BEGIN
/*
Do stuff here.
*/
Select * from Inserted
Select * from deleted
END

Related

SQL Server 2005 'INSTEAD OF' DELETE Trigger - Not deleting Source Records

I created a trigger in SQL Server 2005 that inserts records into a history table whenever a deletion occurs in the source table. The records are getting inserted, but they are not getting deleted from the source table.
Here is my trigger:
CREATE TRIGGER TRG_EdiHistory
ON dbo.EDI10000
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
SET IDENTITY_INSERT dbo.EDI10500 ON;
INSERT INTO EDI10500(File_Id, Tp_Id, File_Name, File_Size, File_Data, Rec_Date, Content_Type, Update_Flag)
SELECT
File_Id, Tp_Id, File_Name, File_Size, File_Data,
Rec_Date, Content_Type, Update_Flag
FROM
DELETED
END
GO
I had to use an INSTEAD OF trigger because my tables contains Image type columns.
Please if anyone has any idea why this is happening.
Thank you.
* UPDATE *
CREATE TRIGGER TRG_EdiHistory
ON dbo.EDI10000
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
SET IDENTITY_INSERT dbo.EDI10500 ON;
INSERT INTO EDI10500 ([File_Id],Tp_Id,[File_Name],File_Size
,File_Data,Rec_Date,Content_Type,Update_Flag)
SELECT [File_Id], Tp_Id, [File_Name], File_Size, File_Data,
Rec_Date, Content_Type, Update_Flag
FROM DELETED
DELETE FROM dbo.EDI10000
WHERE EXISTS (SELECT 1
FROM DELETED
WHERE [FILE_ID] = dbo.EDI10000.[File_Id])
END
GO
Instead of Triggers , fire Instead of the triggering action. In your Case this instead of Trigger fires and Inserts data into your history table.
Note this trigger fires instead of the Delete command. So if you also want to Delete rows you will need to add Delete statement inside this trigger.
Having said this I think instead of Instead Trigger if you simply define an After Trigger with same definition as your this instead of trigger will makes things pretty simple. It will delete the rows from table and then insert rows into your history table as you are expecting it to work.
Update
Since you have mentioned you cannot use Image Data type inside an After Trigger in sql server 2005, I am not aware of this limitation. Well in this case you can simply add a delete statement inside your this existing instead of trigger.
DELETE FROM dbo.table
WHERE EXISTS (SELECT 1
FROM deleted
WHERE Pk_Column = table.PK_Column)
Instead of delete replaces the standard action of the DELETE statement.
http://technet.microsoft.com/en-us/library/ms191208(v=sql.105).aspx
Have a look here how to do what you really want:
How to create a before delete trigger in SQL Server?

triggers in different schemas

I am new to SQL Server.
I have to write a trigger for inserting and updating table in different schema in MS SQL.
Example:
TEMP1 table in one Schema
TEMP2 table in another Schema
How can this be done?
As long the SCHEMAs have the same owner (The AUTHORIZATION bit in CREATE SCHEMA) you'd simply refer to the objects using 2 part names.
See CREATE TRIGGER too
create trigger MyTrigger on Schema1.Table1
for insert
as
set nocount on
insert Schema2.Table2 (...)
select (..) from inserted
go
Not sure I understand the problem completely, but basic syntax would look like this:
create trigger MyTrigger on Schema1.Table1
after insert, update
as
insert Schema2.Table2 values(1, 'test', ...)
update Schema3.Table3
set Name = 'XX'
where Id = 1
go
You have to create multiple triggers to handle different events on different tables.
Refer to CREATE TRIGGER (Transact-SQL).

sql server trigger

I have a table structure like this:
create table status_master
(
Name varchar(40)
status varchar(10)
)
I need to create trigger for status column if the status column value updated value
FAIL then the trigger invoke one insert commant like:
insert into temp value('s',s's')
Could you please any one give me tha idea to solve this?
Not sure what you really want to achieve - but in SQL Server, you have two types of triggers:
AFTER triggers that fire after INSERT, UPDATE, DELETE
INSTEAD OF triggers which can catch the operation (INSERT, UPDATE, DELETE) and do something instead
SQL Server does not have the BEFORE INSERT/UPDATE/DELETE triggers that other RDBMS have.
You can have any number of AFTER triggers, but only one INSTEAD OF trigger for each operation (INSERT, UPDATE, DELETE).
The more common case is the AFTER trigger, something like:
CREATE TRIGGER trgCheckInsertedValues
ON status_master
AFTER INSERT
AS
BEGIN
INSERT INTO dbo.temp(field1, field2, field3)
SELECT i.Name, i.Status
FROM inserted i
WHERE i.Status = 'FAIL'
END
Here, I am inspecting the "inserted" pseudo-table which contains all rows inserted into your table, and for each row that contains "status = FAIL", you'd be inserting some fields into a "temp" table.
Again - not sure what you really want in detail - but this would be the rough outline how to do it in SQL Server T-SQL code.
Marc
Trigger in SQL, is used to trigger a query when any action perform in the particular table like insert,delete,update
http://allinworld99.blogspot.com/2015/04/triggers-in-sql.html
What you're looking for is an INSTEAD OF INSERT, UPDATE trigger. Within your trigger you attempt the insert or update yourself inside a try-catch. If it errors out then you insert those values into your other table (assuming it's a logging table of some sort).
Assuming what you mean is, should the status's new value be FAIL, then what about this:
triggers reference the new record row as 'inserted' and the old one as 'deleted'
CREATE TRIGGER trgCheckInsertedValues ON status_master AFTER INSERT AS
BEGIN
if inserted.status = 'FAIL'
INSERT INTO dbo.temp(field1, field2, field3)
SELECT i.Name, i.Status, 'anything' FROM inserted i

Recursive Update trigger issue in SQL 2005

Below is the code snippet with comments which describes the problem statement. We have an update trigger which internally calls another update trigger on the same table inspite of Recursive Trigger Enabled Property Set to false.
Would like to understand the reason for this as this is causing a havoc in my applications.
/* Drop statements for the table and triggers*/
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo]. [t_upd_TestTrigger_002]'))
DROP TRIGGER [dbo].[t_upd_TestTrigger_002]
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[t_upd_TestTrigger_002]'))
DROP TRIGGER [dbo].[t_upd_TestTrigger_001]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestTrigger]') AND type in (N'U'))
DROP TABLE [dbo].[TestTrigger]
CREATE TABLE [dbo].[TestTrigger] /*Creating a test table*/
(
[InternalKey] INT NOT NULL,
[UserModified] varchar(50) DEFAULT SUSER_SNAME()
)
/* Please run the snippet below as seperate batch, else you will get
an error that 'CREATE TRIGGER' must be the first statement in a
query batch.
CREATING A UPDATE TRIGGER FOR THE TEST TABLE
*/
CREATE TRIGGER [t_upd_TestTrigger_001] ON [dbo].[TestTrigger]
FOR UPDATE
AS
BEGIN
--This trigger has some business logic which gets executed
print 'In Trigger 001 '
END
/* Please run the snippet below as separate batch, else you will
get an error that 'CREATE TRIGGER' must be the first statement
in a query batch.
CREATING Another UPDATE TRIGGER FOR THE TEST TABLE
This trigger updates the audit fields in the table and it has to be
a separate trigger, We cannot combine this with other update triggers -
So table TestTrigger will have two FOR UPDATE triggers
*/
CREATE TRIGGER [t_upd_TestTrigger_002] ON [dbo].[TestTrigger]
FOR UPDATE
AS
print 'bad guy starts'
UPDATE SRC
SET UserModified = SUSER_SNAME()
FROM inserted AS INS
INNER JOIN dbo.[TestTrigger] AS SRC
ON INS.InternalKey = SRC.InternalKey
print 'bad guy ends'
/* INSERTING TEST VALUE IN THE TEST TRIGGER TABLE*/
INSERT INTO dbo.[TestTrigger](InternalKey,UserModified)
SELECT 1 ,'Tester1' UNION ALL
SELECT 2,'Tester2' UNION ALL
SELECT 3 ,'Tester3'
/* TestTrigger table has 3 records, we will update the InternalKey
of first record from 1 to 4. We would expect following actions
1) [t_upd_TestTrigger_001] to be executed once
2) [t_upd_TestTrigger_002] to be executed once
3) A message that (1 row(s) affected) only once.
On Execution, i find that [t_upd_TestTrigger_002] internally triggers
[t_upd_TestTrigger_001].
Please note Database level property Recursive Triggers enabled is
set to false.
*/
/*UPDATE THE TABLE SEE THE MESSAGE IN RESULT WINDOW*/
UPDATE dbo.[TestTrigger]
SET InternalKey = 4
WHERE InternalKey = 1
"Recursive Triggers enabled" does not affect transitive triggers.
Which means that if trigger A updates a table in a manner that activates trigger B, and trigger B updates the same table, so that trigger A is run again, SQL Server has no way of detecting and inhibiting this endless loop. Especially since trigger B can update other tables, and a trigger on them could update the original table again - this could become as complex as you like.
Eventually, the trigger nesting level limit will be reached, and the loop stops.
I suspect that both of your triggers update the source table in some way. SQL Server can only detect recursive triggers if a trigger is activating itself. I suppose that's not the case for you. Restructuring the triggers is the only clean way out.
As a (hackery) idea: You could append a field to the table (data-type and value is irrelevant) that is updated by no operation but by triggers. Then change your second-order triggers to update that field. Add an IF UPDATE() check for that field to your first-order trigger. Prevent the now redundant update if the field has been set. If that makes sense. ;-)
MSDN: Using Nested Triggers, see sections "Direct recursion" and "Indirect recursion".
You can use IF UPDATE(), as Tomalak described, to skip trigger logic if UserModified is being updated.
Another possibility is to move the UserModified column to a separate table to avoid recursion.
If you want to stop this kind of behaviour across the database totally, "To disable indirect recursion, set the nested triggers server option to 0 using sp_configure. For more information, see Using Nested Triggers."
Of course, there's always the consideration that you may want to actually use nested triggers.

How can I do a BEFORE UPDATED trigger with sql server?

I'm using Sqlserver express and I can't do before updated trigger. There's a other way to do that?
MSSQL does not support BEFORE triggers. The closest you have is INSTEAD OF triggers but their behavior is different to that of BEFORE triggers in MySQL.
You can learn more about them here, and note that INSTEAD OF triggers "Specifies that the trigger is executed instead of the triggering SQL statement, thus overriding the actions of the triggering statements." Thus, actions on the update may not take place if the trigger is not properly written/handled. Cascading actions are also affected.
You may instead want to use a different approach to what you are trying to achieve.
It is true that there aren't "before triggers" in MSSQL. However, you could still track the changes that were made on the table, by using the "inserted" and "deleted" tables together. When an update causes the trigger to fire, the "inserted" table stores the new values and the "deleted" table stores the old values. Once having this info, you could relatively easy simulate the "before trigger" behaviour.
Can't be sure if this applied to SQL Server Express, but you can still access the "before" data even if your trigger is happening AFTER the update. You need to read the data from either the deleted or inserted table that is created on the fly when the table is changed. This is essentially what #Stamen says, but I still needed to explore further to understand that (helpful!) answer.
The deleted table stores copies of the affected rows during DELETE and
UPDATE statements. During the execution of a DELETE or UPDATE
statement, rows are deleted from the trigger table and transferred to
the deleted table...
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table...
https://msdn.microsoft.com/en-us/library/ms191300.aspx
So you can create your trigger to read data from one of those tables, e.g.
CREATE TRIGGER <TriggerName> ON <TableName>
AFTER UPDATE
AS
BEGIN
INSERT INTO <HistoryTable> ( <columns...>, DateChanged )
SELECT <columns...>, getdate()
FROM deleted;
END;
My example is based on the one here:
http://www.seemoredata.com/en/showthread.php?134-Example-of-BEFORE-UPDATE-trigger-in-Sql-Server-good-for-Type-2-dimension-table-updates
sql-server triggers
T-SQL supports only AFTER and INSTEAD OF triggers, it does not feature a BEFORE trigger, as found in some other RDBMSs.
I believe you will want to use an INSTEAD OF trigger.
All "normal" triggers in SQL Server are "AFTER ..." triggers. There are no "BEFORE ..." triggers.
To do something before an update, check out INSTEAD OF UPDATE Triggers.
To do a BEFORE UPDATE in SQL Server I use a trick. I do a false update of the record (UPDATE Table SET Field = Field), in such way I get the previous image of the record.
Remember that when you use an instead trigger, it will not commit the insert unless you specifically tell it to in the trigger. Instead of really means do this instead of what you normally do, so none of the normal insert actions would happen.
Full example:
CREATE TRIGGER [dbo].[trig_020_Original_010_010_Gamechanger]
ON [dbo].[T_Original]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Old_Gamechanger int;
DECLARE #New_Gamechanger int;
-- Insert statements for trigger here
SELECT #Old_Gamechanger = Gamechanger from DELETED;
SELECT #New_Gamechanger = Gamechanger from INSERTED;
IF #Old_Gamechanger != #New_Gamechanger
BEGIN
INSERT INTO [dbo].T_History(ChangeDate, Reason, Callcenter_ID, Old_Gamechanger, New_Gamechanger)
SELECT GETDATE(), 'Time for a change', Callcenter_ID, #Old_Gamechanger, #New_Gamechanger
FROM deleted
;
END
END
The updated or deleted values are stored in DELETED. we can get it by the below method in trigger
Full example,
CREATE TRIGGER PRODUCT_UPDATE ON PRODUCTS
FOR UPDATE
AS
BEGIN
DECLARE #PRODUCT_NAME_OLD VARCHAR(100)
DECLARE #PRODUCT_NAME_NEW VARCHAR(100)
SELECT #PRODUCT_NAME_OLD = product_name from DELETED
SELECT #PRODUCT_NAME_NEW = product_name from INSERTED
END