SQL Server Trigger Çeşitleri

Trigger veritabanı sunucusunda bir olay gerçekleştiğinde başka bir olayı tetiklemek için kullanılır. DML,DDL ve Logon Trigger olmak üzere 3 tip trigger vardır.

  1. DML(Data Manipulation Language) Trigger
  2. DDL(Data Definition Language) Trigger
  3. Logon Trigger

DML(Data Manipulation Language) Trigger: Veritabanına yapılan INSERT,UPDATE ve DELETE işlemlerini içerir. DML Trigger’ın yapacağı işi PRIMARY KEY, FOREIGN KEY, UNIQUE constraint ya da CHECK CONSTRAINTS’ler ile de yapabilirsiniz.  “ Primary Key ve Foreign Key “,” Primary Key ve Unique Constaint’in farkları ” ve “ CHECK CONSTRAINT ” isimli makalelerimde bu kavramlar hakkında bilgi bulabilirsiniz. DML Trigger’ları, yukarıda bahsedilen CONSTRAINT’ler fonksiyonel ihtiyaçları karşılamadığında kullanabilirsiniz.

DML trigger’larının aşağıdaki örneklerde kullanılacak inserted ve deleted isimli tabloları vardır. Örneğin bir insert işlemi geldiğinde inserted tablosunda insert edilen kayıtları, bir update ya da delete işlemi geldiğinde deleted tablosunda da silinen kayıtları bulabileceksiniz.

2 tip DML Trigger vardır.

  1. AFTER ya da FOR: Veritabanına yapılan herhangi bir DML işleminin(INSERT,UPDATE ve DELETE) tamamlanmasından sonra tetiklenir. SQL Server’ın önceki sürümlerinde FOR olarak geçiyordu bu yüzden hala FOR desteği devam ediyor. AFTER ve FOR aynı anlama geliyor. AFTER INSERT,AFTER UPDATE ve AFTER DELETE için birer örnek yapalım. Yapacağımız her örnek için aşağıda create script’i olan tabloyu kullanacağız.

CREATE TABLE [dbo].[TriggerOrnegi](
[ID] [int] NULL,
[AdSoyad] [varchar](100) NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[TriggerOrnegiBackup](
[ID] [int] NULL,
[AdSoyad] [varchar](100) NULL
) ON [PRIMARY]

Trigger’ı da aşağıdaki şekilde oluşturalım.

CREATE TRIGGER Trg_After_Insert
ON [dbo].[TriggerOrnegi]
AFTER INSERT AS
BEGIN
INSERT INTO [dbo].[TriggerOrnegiBackup]
SELECT * FROM INSERTED
END

Şimdi TriggerOrnegi isimli tablomuza aşağıdaki script yardımıyla bir insert gerçekleştirelim. Ardından da TriggerOrnegi ve TriggerOrnegiBackup tabloları select çekerek trigger’ımızın çalışıp çalışmadığını kontrol edelim.

INSERT INTO [dbo].[TriggerOrnegi]([ID],[AdSoyad])VALUES(1,'Nurullah ÇAKIR')
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerOrnegiBackup]

Aşağıdaki ekran görüntüsünden de görebileceğiniz üzere trigger başarılı bir şekilde çalıştı.

CREATE TRIGGER Trg_After_Update
ON [dbo].[TriggerOrnegi]
AFTER UPDATE AS
BEGIN
UPDATE [dbo].[TriggerOrnegiBackup]
SET    [dbo].[TriggerOrnegiBackup].AdSoyad = inserted.AdSoyad
FROM   [dbo].[TriggerOrnegiBackup]
       JOIN inserted
         ON [dbo].[TriggerOrnegiBackup].ID = inserted.ID 
END

Trigger’ın doğru çalıştığını kontrol etmek için TriggerOrnegi tablosundaki AdSoyad kolonunu update eden ve sonra her iki tablodan da select çeken aşağıdaki sorguyu çalıştıralım.

UPDATE [dbo].[TriggerOrnegi]   SET [AdSoyad] = 'Nurullah ÇAKIR Kim?' WHERE AdSoyad='Nurullah ÇAKIR'
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerOrnegiBackup]

Gördüğünüz gibi trigger doğru bir şekilde çalıştı.

CREATE TRIGGER Trg_After_Delete
ON [dbo].[TriggerOrnegi]
AFTER DELETE AS
BEGIN
DELETE FROM [dbo].[TriggerOrnegiBackup] WHERE [dbo].[TriggerOrnegiBackup].ID IN (SELECT ID FROM DELETED)
END

TriggerOrnegi tablomuzdan aşağıdaki script yardımıyla bir kayıt silerek trigger’ın çalışıp çalışmadığını kontrol edelim.

DELETE FROM [dbo].[TriggerOrnegi]  WHERE [AdSoyad] = 'Nurullah ÇAKIR Kim?'
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerOrnegiBackup]

Gördüğünüz gibi TriggerOrnegi tablomuzdan bir kayıt sildiğimizde ilgili kayıt TriggerOrnegiBackup tablosundan da silindi.

  1. INSTEAD OF: AFTER trigger’ında DML işlemleri başarılı bir şekilde gerçekleştikten sonra trigger çalışıyordu. INSTEAD OF trigger’ında ise DML işlemi gerçekleşmeden bu trigger tetiklenir. Bir çok amaçla bu trigger’ı kullanabilirsiniz biz bu makalede tek bir amacı benimseyen örnekler yapacağız. INSTEAD OF INSERT, INSTEAD OF UPDATE ve INSTEAD OF DELETE trigger çeşitlerinin nasıl oluştuğuna bakalım.

CREATE TRIGGER Trg_InsteadOf_Insert ON [dbo].[TriggerOrnegi]
INSTEAD OF INSERT
AS BEGIN
INSERT INTO [dbo].[TriggerOrnegi]([ID],[AdSoyad])Select ID+1,AdSoyad FROM inserted
INSERT INTO [dbo].[TriggerOrnegiBackup]([ID],[AdSoyad])Select ID+1,AdSoyad FROM inserted
END

Aşağıdaki script yardımıyla da TriggerOrnegi isimli tabloya insert işlemi yapıp iki tabloyada select çekerek sonucunu görelim. (Öncesinde AFTER örneğinde oluşturduğumuz tüm trigger’ları ve kayıtları silmeyi unutmayın.)

INSERT INTO [dbo].[TriggerOrnegi]([ID],[AdSoyad])VALUES(1,'Nurullah ÇAKIR')
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerOrnegiBackup]

Gördüğünüz gibi ID değerini 1 olarak insert etmemize rağmen değerini bir arttırıp 2 yaptık ve iki tabloya da bu şekilde insert işlemini gerçekleştirdik.

CREATE TABLE [dbo].[TriggerLog](
[IDMevcutHali] [int] NULL,
[IDUpdateEdilmekIstenenHali] [int] null,
[Aciklama] [varchar](500) NULL
) ON [PRIMARY]

Oluşturacağımız Trigger,TriggerOrnegi isimli tablomuzda ID kolonuna update gelirse update’i rollback yapacak ve yukarıda oluşturduğumuz TriggerLog tablosuna da değiştirilmeye çalışılan ID değerini ve açıklamasını girecek. AdSoyad kolonuna update gelirse de bu update değerini gerçekleştirecek ve hangi kaydın update edildiğini açıklaması ile beraber TriggerLog tablosuna girecek. Oluşturacağımız trigger’ın create script’i aşağıdaki gibidir.

CREATE TRIGGER Trg_InsteadOf_Update
       ON [dbo].[TriggerOrnegi]
INSTEAD OF UPDATE
AS
BEGIN
       DECLARE @ID INT, @AdSoyad VARCHAR(100)
       SELECT @ID= INSERTED.ID,
              @AdSoyad = INSERTED.AdSoyad
       FROM INSERTED

       IF UPDATE(ID)
       BEGIN
              RAISERROR('ID Değeri Update Edilemez.', 16 ,1)
              ROLLBACK
              INSERT INTO [dbo].[TriggerLog]
              VALUES(@ID, 'ID değeri update edilemez.')
       END
       ELSE
       BEGIN
              UPDATE [dbo].[TriggerOrnegi]
              SET AdSoyad = @AdSoyad
              WHERE ID = @ID
              INSERT INTO [dbo].[TriggerLog]
              VALUES(@ID, 'AdSoyad kolonu update edildi.')
       END
END

Şimdi ilk script’imizde TriggerOrnegi tablosundaki ID kolonunu update etmeye çalışalım ve TriggerOrnegi ve TriggerLog tablolarına select çekerek trigger’ın çalışıp çalışmadığını kontrol edelim. Önceki trigger’ları silmeyi unutmayın.

UPDATE [dbo].[TriggerOrnegi] SET ID = 3 Where ID=2
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerLog]

Yukarıdaki script’i çalıştırdığımızda aşağıdaki gibi ID değerinin update edilemeyeceğini ve update işleminin rollback olduğunu belirten bir mesaj aldık.

Messages kısmından hemen solundaki sekmedeki Result sekmesine geçtiğimizde de kaydın update olmadığını ve hangi değere update edilmek istendiği bilgisini TriggerLog tablosunda görebiliriz.

Şimdi AdSoyad kolonunu aşağıdaki script yardımıyla update etmeye çalışalım.

UPDATE [dbo].[TriggerOrnegi] SET AdSoyad = 'Nurullah ÇAKIR Kim?' Where ID=2
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerLog]

Aşağıda gördüğünüz gibi update işlemi gerçekleşti ve daha sonra TriggerLog tablosuna ilgili Log kaydı düşüldü.

CREATE TRIGGER [dbo].[Trg_InsteadOf_Delete]
       ON [dbo].[TriggerOrnegi]
INSTEAD OF DELETE
AS
BEGIN
       DECLARE @ID INT
       SELECT @ID = DELETED.ID      
       FROM DELETED
       IF @ID = 2
       BEGIN
              RAISERROR('Nurullah ÇAKIR kişisine ait kayıt silinemez.',16 ,1)
              ROLLBACK
              INSERT INTO [dbo].[TriggerLog]
              VALUES(@ID,NULL, 'Nurullah ÇAKIR kişisine ait kayıt silinmek istendi.')
       END
       ELSE
       BEGIN
              DELETE FROM [dbo].[TriggerOrnegi]
              WHERE ID = @ID
              INSERT INTO [dbo].[TriggerLog]
              VALUES(@ID,NULL, 'Instead Of Delete')
       END
END

Nurullah ÇAKIR isimli kişiye ait ID değeri 2 olan kaydı silmeye çalışalım. Tabi önce diğer trigger’ları silmeyi unutmayın.

DELETE FROM [dbo].[TriggerOrnegi] Where ID=2
GO
Select * FROM [dbo].[TriggerOrnegi]
GO
Select * FROM [dbo].[TriggerLog]

Script’i çalıştırdığımızda bize aşağıdaki gibi bir mesaj verdi.

Results sekmesine geçtiğimizde de kaydın silinmediğini ve Log tablosuna da ilgili log kaydının düştüğünü görebiliriz. Eğer ID değeri 2’nin dışındaki bir kayıt silinmek isteseydi silme işlemi gerçekleşecekti.

DDL(Data Definition Language) Trigger: Veritabanına yapılan modifikasyon işlemleri gerçekleştiğinde tetiklenebilir. Hangi işlemden sonra tetikleneceğini Trigger’ı oluştururken belirtiriz. Veritabanı modifikasyon işlemlerine aşağıda birkaç örnek verdim.

Veritabanı içindeki modifikasyon işlemlerini içeren bir  örnek yapmak için Test veritabanımızda aşağıdaki script yardımıyla bir stored procedure, bir view, bir function oluşturalım.

--Örnek Stored Procedure Oluşturuyoruz.
CREATE PROCEDURE OrnekStoredProcedure
AS
BEGIN
SET NOCOUNT ON;
END

--Örnek Function Oluşturuyoruz
CREATE FUNCTION OrnekFunction(@a int)
RETURNS TABLE
AS
RETURN (SELECT 0 AS dönecektablo)

--Örnek View Oluşturuyoruz
CREATE VIEW [dbo].[OrnekView]
AS
SELECT dbo.TriggerOrnegi.*
FROM     dbo.TriggerOrnegi

Ornek objelerimizi oluşturduktan sonra aşağıdaki gibi bir Database Trigger oluşturalım.

Tanımlayacağımız trigger bu veritabanında TABLE,FUNCTION,VIEW ya da STORED PROCEDURE oluşmasını ve mevcut olanlarında  silinmesini ya da değiştirilmesini engelleyecek.

CREATE TRIGGER DDLTriggerOrnek
ON DATABASE
FOR CREATE_TABLE,DROP_TABLE, ALTER_TABLE,
CREATE_FUNCTION,ALTER_FUNCTION,DROP_FUNCTION,
CREATE_VIEW,ALTER_VIEW,DROP_VIEW,
CREATE_PROCEDURE,ALTER_PROCEDURE,DROP_PROCEDURE AS
PRINT 'Bu Veritabanında Modifikasyon İşlemleri Yapılamaz'
ROLLBACK

Trigger’ı oluşturduktan sonra yukarıda bahsedilen objeleri silmeyi ya da değiştirmeyi ya da yeni bir tane oluşturmayı deneyerek sonucu görebilirsiniz.

Örnek olarak oluşturduğumuz view’i silmeyi deneyelim.

DROP VIEW [dbo].[OrnekView]
GO

DDL Trigger ile neleri kontrol edebileceğiniz öğrenmek için aşağıdaki script’i çalıştırmalısınız.

select * from sys.trigger_event_types

Logon Trigger: Kullanıcı login olmak için giriş yaptığında login ve şifre bilgileri kontrol edildikten sonra eğer bağlantı bilgileri doğruysa bağlantı gerçekleşmeden tetiklenir. Başarısız bağlantılar için bu yapıyı kullanamazsınız. Bunun yerine SQL Server Alertleri kullanmalısınız.

Failed Logon’ları Mail Atacak Alert Oluşturmak ” isimli makalemde bu konuyu anlattım. “ Always On Alert Sistemi ” ve “ Yeni Kurulumda yapılması gereken konfigurasyonlar ” isimli makalelerimde alertler ile ilgili daha detay bilgi bulabilirsiniz.

Başarılı girişleri loglamak ya da bazı loginlerin yetkilerini sınırlandırmak amaçlı kullanılabilir. Bu iki konuyla ilgili 2 tane örnek yapalım.

Örnek1:

Aşağıdaki script ile master veritabanında başarılı loginleri tutmak için bir tablo oluşturuyoruz ve Logon Trigger ile başarılı girişleri logluyoruz.

Çok fazla login işlemi master veritabanınızı şişirebilir. Bu yüzden başka bir veritabanına kaydetmeniz ya da bu tabloda eskiyen kayıtları silmeniz gerekebilir.

CREATE TABLE Login_Info
(
       Login_Name           NVARCHAR(256),
       Login_Time           DATETIME,
       Host_Name            NVARCHAR(128)
)
CREATE TRIGGER Trigger_Log
ON ALL SERVER WITH EXECUTE AS 'sa'
FOR LOGON
AS
BEGIN
       INSERT INTO master..Login_Info
       SELECT ORIGINAL_LOGIN(), GETDATE(),
       EVENTDATA().value('(/EVENT_INSTANCE/ClientHost)[1]','NVARCHAR(128)')
END

Logon Trigger’ı aşağıdaki script yardımıyla silebilirsiniz.

DROP TRIGGER Trigger_Log ON ALL SERVER

Örnek2:

Aşağıdaki script ile TestLogin isimli bir login oluşturuyoruz ve bu login’in session sayısı 3 olduğunda trigger’ın tetiklenerek 3 den fazla connection açmasını engellemiş oluyoruz.

USE master; 
GO 
CREATE LOGIN TestLogin WITH PASSWORD = 'Test123'  
GO 
GRANT VIEW SERVER STATE TO TestLogin; 
GO 
CREATE TRIGGER Trigger_ConnectionLimit 
ON ALL SERVER
FOR LOGON 
AS 
BEGIN 
IF ORIGINAL_LOGIN()= 'TestLogin' AND 
    (SELECT COUNT(*) FROM sys.dm_exec_sessions 
            WHERE is_user_process = 1 AND 
                original_login_name = 'TestLogin') > 3 
    ROLLBACK; 
END;

TestLogin ile login olduktan sonra new query diyerek birkaç tane query ekranı açmaya çalışın. Üçüncü query ekranını açmaya çalıştığınızda aşağıdaki gibi hata verecektir.

Logon Trigger’ı aşağıdaki script yardımıyla silebilirsiniz.

DROP TRIGGER Trigger_ConnectionLimit ON ALL SERVER

Tablo seviyesinde Trigger’ları aşağıdaki gibi Triggers altında görebilirsiniz.

Veritabanı seviyesindeki Trigger’ları aşağıdaki gibi Programmability->Database Triggers altından görebilirsiniz.

Server Seviyesindeki Trigger’ları aşağıdaki gibi Server Objects’in altından görebilirsiniz.

Loading