Introduction
A trigger is a database object. A trigger is a special type of Stored Procedure that is automatically executed. We can create a DML, DDL or logon trigger in SQL Server 2012. Data Manipulation Language (DML) triggers execute when someone attempts to modify data through a DML event. DML events are INSERT, UPDATE or DELETE statements on a view or table. The same as Data Definition Language (DDL), triggers execute in response to a variety of DDL events. These events are CREATE, ALTER and DROP statements and certain system Stored Procedures that perform like DDL operations. A Logon Trigger fires in response to the LOGON event raised when the user session is started.
Problem statement
Suppose, I have two trigger, Trigger A and Trigger B, define on my database table that are fire on the table for same action (INSERT, UPDATE or DELETE). My Business Logic enforces me, Trigger A fire first. In other word, I want to fire "trigger-A" before "trigger-B". Can we set a DML trigger (AFTER trigger) firing order in SQL Server? If yes then how can we manage the trigger firing order?
Solution
Multiple Triggers on a Database table for the same action are not fired in a guaranteed order. It is possible to set the firing order for two triggers (AFTER Trigger) using the system store procedure "sp_settriggerorder". This System Stored Procedure cannot be used with an INSTEAD OF TRIGGER and it throws an error if we are trying to set the same order on these triggers.
Syntax
sp_settriggerorder @triggername , @order
@stmttype, @namespace
Parameter Description
@triggername
This is a name of a trigger of which the order is to be set. Here the trigger name is the schema name. This procedure returns an error if the trigger does not exist in the schema or the trigger type is INSTEAD OF. The Trigger Schema cannot specify DDL or LOGON triggers.
@order
This is the value of the new order of the triggers. This parameter is VARCHAR (10). Possible values of this parameter are First, Last and None.
Order Value |
Description |
First |
Set trigger on First order |
Last |
Set trigger on Last order |
None |
Undefined order. |
@stmttype
Statement Type. This is VARCHAR (50). This is a SQL Statement that fires the trigger like INSERT, UPDATE, DELETE, and so on.
@namespace
It is used when the trigger is a DDL type. It specifies whether the trigger name is created within the database or the SQL Server scope. Possible values of this parameter are "DATABASE", "SERVER" or "NULL". If the trigger is created for the LOGON trigger then "SERVER" must be specified.
There is only one First and one Last trigger for each statement type per table for a DML trigger. If the First trigger is already defined on the table for any statement type (DATABASE or SERVER) then we cannot create a new trigger as First for the same table and for the same statement type. The same thing is applied to the Last trigger too.
If the DDL trigger with any statement type (DATABASE or SERVER) is on the same event then we can specify that both triggers are a First or Last trigger. However a SERVER scoped trigger is always fired first.
Example
I have a table called "EmployeeMaster" that has two AFTER INSERT triggers. My business logic specifies that trigger 1 must always be fired before trigger 2.
Let create example step-by-step
Crate EmployeeMaster table
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[EmployeeMaster]') AND type in (N'U'))
DROP TABLE [dbo].[EmployeeMaster]
GO
CREATE TABLE [dbo].[EmployeeMaster](
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[EmployeeCode] [varchar](10) NULL,
[EmployeeName] [varchar](100) NULL,
CONSTRAINT [PK_EmployeeMaster] PRIMARY KEY CLUSTERED
(
[EmployeeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Create TriggerOrderTest table
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TriggerOrderTest]') AND type in (N'U'))
DROP TABLE [dbo].[TriggerOrderTest]
GO
CREATE TABLE [dbo].[TriggerOrderTest](
[TriggerName] [varchar](50) NULL,
[Type] [varchar](50) NULL,
[Fireorder] [varchar](50) NULL
) ON [PRIMARY]
Create two Triggers on the EmployeeMaster Table for INSERT ("Trigger A" and "Trigger B").
In these triggers, I am just inserting a new record in the TriggerOrderTest table with the trigger name type and firing order.
--Create AFTER Trigger on EmployeeMaster table for INSERT (Trigger A)
CREATE TRIGGER dbo.trgInsertOnEmployeeMaster1
ON dbo.EmployeeMaster
AFTER INSERT
AS
BEGIN
INSERT INTO [dbo].[TriggerOrderTest] ([TriggerName],[Type],[Fireorder])
VALUES ('trgInsertOnEmployeeMaster1','DATABASE','First')
END
--Create AFTER Trigger on EmployeeMaster table for INSERT (Trigger B)
CREATE TRIGGER dbo.trgInsertOnEmployeeMaster2
ON dbo.EmployeeMaster
AFTER INSERT
AS
BEGIN
INSERT INTO [dbo].[TriggerOrderTest] ([TriggerName],[Type],[Fireorder])
VALUES ('trgInsertOnEmployeeMaster2','DATABASE','Last')
END
Set triggers order
EXEC sp_settriggerorder 'dbo.trgInsertOnEmployeeMaster1', 'First', 'INSERT'
EXEC sp_settriggerorder 'dbo.trgInsertOnEmployeeMaster2', 'Last', 'INSERT'
Insert some test data into EmployeeMaster table
INSERT INTO [dbo].[EmployeeMaster] ([EmployeeCode],[EmployeeName])
VALUES('E0011', 'Jigneh Trivedi')
Output or Result
Now the trigger firing order has been corrected.
SELECT * FROM TriggerOrderTest
Limitation
If we have more than two triggers then we can only specify the FIRST and LAST trigger firing order. So using this feature we can order up to three triggers on the table for the same action. If we have 4 or more triggers (on the same action) on the database table , then we can only set the trigger order FIRST and LAST, another two are fired randomly (there is no guaranteed order for the other two triggers).
This feature is not useful for INSTEAD OF triggers.
Conclusion
Using the System Stored Procedure "sp_settriggerorder", we can set the firing order of a trigger with certain limitations.