0% found this document useful (0 votes)
33 views

23 - Building and Optimizing Triggers in SQL Server

Triggers allow running code automatically when data is inserted, updated or deleted from database tables. The document discusses creating different types of triggers for various purposes like auditing, preventing changes, and tracking history. It also covers best practices for managing triggers through operations like disabling, enabling, modifying and removing triggers.

Uploaded by

massyweb
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views

23 - Building and Optimizing Triggers in SQL Server

Triggers allow running code automatically when data is inserted, updated or deleted from database tables. The document discusses creating different types of triggers for various purposes like auditing, preventing changes, and tracking history. It also covers best practices for managing triggers through operations like disabling, enabling, modifying and removing triggers.

Uploaded by

massyweb
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 8

/*

Introduction to Triggers
*/

---- Creating trigger

-- Create a new trigger that fires when deleting data


create TRIGGER PreventDiscountsDelete
ON Discounts
-- The trigger should fire instead of DELETE
instead of DELETE
AS
PRINT 'You are not allowed to delete data from the Discounts table.';

---- Practicing creating triggers

-- Set up a new trigger


create TRIGGER OrdersUpdatedRows
ON Orders
-- The trigger should fire after UPDATE statements
after UPDATE
-- Add the AS keyword before the trigger body
AS
-- Insert details about the changes to a dedicated table
insert INTO OrdersUpdate(OrderID, OrderDate, ModifyDate)
SELECT OrderID, OrderDate, GETDATE()
FROM inserted;

---- Creating a trigger to keep track of data changes

-- Create a new trigger


CREATE TRIGGER ProductsNewItems
ON Products
AFTER insert
AS
-- Add details to the history table
INSERT INTO ProductsHistory(Product, Price, Currency, FirstAdded)
SELECT Product, Price, Currency, GETDATE()
FROM inserted;

---- Triggers vs. stored procedures

-- Run an update for some of the discounts


update Discounts
SET Discount = Discount + 1
WHERE Discount <= 5;

-- Verify the trigger ran successfully


select * FROM DiscountsHistory;

---- Triggers vs. computed columns

-- Add the following rows to the table


INSERT INTO SalesWithoutPrice (Customer, Product, Currency, Quantity)
VALUES ('Fruit Mag', 'Pomelo', 'USD', 200),
('VitaFruit', 'Avocado', 'USD', 400),
('Tasty Fruits', 'Blackcurrant', 'USD', 1100),
('Health Mag', 'Kiwi', 'USD', 100),
('eShop', 'Plum', 'USD', 500);

-- Verify the results after the INSERT


SELECT * FROM SalesWithoutPrice;

/*
Classification of Triggers
*/

---- Tracking retired products

-- Create the trigger


CREATE TRIGGER TrackRetiredProducts
ON Products
AFTER DELETE
AS
INSERT INTO RetiredProducts (Product, Measure)
SELECT Product, Measure
FROM deleted;

---- Tracking retired products

-- Create the trigger


CREATE TRIGGER TrackRetiredProducts
ON Products
AFTER DELETE
AS
INSERT INTO RetiredProducts (Product, Measure)
SELECT Product, Measure
FROM deleted;

---- The TrackRetiredProducts trigger in action

-- Remove the products that will be retired


DELETE FROM Products
WHERE Product IN ('Cloudberry', 'Guava', 'Nance', 'Yuzu');

-- Verify the output of the history table


SELECT * FROM RetiredProducts;

---- Practicing with AFTER triggers

-- Create a new trigger for canceled orders


CREATE TRIGGER KeepCanceledOrders
ON Orders
AFTER DELETE
AS
INSERT INTO CanceledOrders
SELECT * FROM deleted;
-- Create a new trigger to keep track of discounts
CREATE TRIGGER CustomerDiscountHistory
ON Discounts
AFTER INSERT
AS
-- Store old and new values into the `DiscountsHistory` table
INSERT INTO DiscountsHistory (Customer, OldDiscount, NewDiscount, ChangeDate)
SELECT i.Customer, d.Discount, i.Discount, GETDATE()
FROM inserted AS i
INNER JOIN deleted AS d ON i.Customer = d.Customer;

-- Notify the Sales team of new orders


CREATE TRIGGER NewOrderAlert
ON Orders
AFTER INSERT
AS
EXECUTE SendEmailtoSales;

---- Preventing changes to orders

-- Create the trigger


CREATE TRIGGER PreventOrdersUpdate
ON Orders
INSTEAD OF UPDATE
AS
RAISERROR ('Updates on "Orders" table are not permitted.
Place a new order to add new products.', 16, 1);

---- Creating the PreventNewDiscounts trigger

-- Create a new trigger


CREATE TRIGGER PreventNewDiscounts
ON Discounts
INSTEAD OF INSERT
AS
RAISERROR ('You are not allowed to add discounts for existing customers.
Contact the Sales Manager for more details.', 16, 1);

---- Tracking table changes

-- Create the trigger to log table info


CREATE TRIGGER TrackTableChanges
ON DATABASE
FOR CREATE_TABLE,
ALTER_TABLE,
DROP_TABLE
AS
INSERT INTO TablesChangeLog (EventData, ChangedBy)
VALUES (EVENTDATA(), USER);

---- Preventing table deletion

-- Add a trigger to disable the removal of tables


CREATE TRIGGER PreventTableDeletion
ON DATABASE
FOR DROP_TABLE
AS
RAISERROR ('You are not allowed to remove tables from this database.', 16,
1);
-- Revert the statement that removes the table
ROLLBACK;

---- Enhancing database security

-- Save user details in the audit table


INSERT INTO ServerLogonLog (LoginName, LoginDate, SessionID, SourceIPAddress)
SELECT ORIGINAL_LOGIN(), GETDATE(), @@SPID, client_net_address
-- The user details can be found in SYS.DM_EXEC_CONNECTIONS
FROM SYS.DM_EXEC_CONNECTIONS WHERE session_id = @@SPID;

/*
Trigger Limitations and Use Cases
*/

---- Creating a report on existing triggers

-- Gather information about database triggers


SELECT name AS TriggerName,
parent_class_desc AS TriggerType,
create_date AS CreateDate,
modify_date AS LastModifiedDate,
is_disabled AS Disabled,
is_instead_of_trigger AS InsteadOfTrigger,
-- Get the trigger definition by using a function
OBJECT_DEFINITION (object_id)
FROM sys.triggers
UNION ALL
-- Gather information about server triggers
SELECT name AS TriggerName,
parent_class_desc AS TriggerType,
create_date AS CreateDate,
modify_date AS LastModifiedDate,
is_disabled AS Disabled,
0 AS InsteadOfTrigger,
-- Get the trigger definition by using a function
OBJECT_DEFINITION (object_id)
FROM sys.server_triggers
ORDER BY TriggerName;

---- Keeping a history of row changes

-- Create a trigger to keep row history


CREATE TRIGGER CopyCustomersToHistory
ON Customers
-- Fire the trigger for new and updated rows
AFTER INSERT, UPDATE
AS
INSERT INTO CustomersHistory (CustomerID, Customer, ContractID, ContractDate,
Address, PhoneNo, Email, ChangeDate)
SELECT CustomerID, Customer, ContractID, ContractDate, Address, PhoneNo,
Email, GETDATE()
-- Get info from the special table that keeps new rows
FROM inserted;

---- Table auditing using triggers

-- Add a trigger that tracks table changes


create trigger OrdersAudit
ON Orders
after INSERT, update, delete
AS
DECLARE @Insert BIT = 0;
DECLARE @Delete BIT = 0;
IF EXISTS (SELECT * FROM inserted) SET @Insert = 1;
IF EXISTS (SELECT * FROM deleted) SET @Delete = 1;
INSERT INTO TablesAudit (TableName, EventType, UserAccount, EventDate)
SELECT 'Orders' AS TableName
,CASE WHEN @Insert = 1 AND @Delete = 0 THEN 'INSERT'
WHEN @Insert = 1 AND @Delete = 1 THEN 'UPDATE'
WHEN @Insert = 0 AND @Delete = 1 THEN 'DELETE'
END AS Event
,ORIGINAL_LOGIN() AS UserAccount
,GETDATE() AS EventDate;

---- Preventing changes to Products

-- Prevent any product changes


CREATE TRIGGER PreventProductChanges
ON Products
INSTEAD OF UPDATE
AS
RAISERROR ('Updates of products are not permitted. Contact the database
administrator if a change is needed.', 16, 1);

---- Checking stock before placing orders

-- Create a new trigger to confirm stock before ordering


CREATE TRIGGER ConfirmStock
ON Orders
INSTEAD OF INSERT
AS
IF EXISTS (SELECT *
FROM Products AS p
INNER JOIN inserted AS i ON i.Product = p.Product
WHERE p.Quantity < i.Quantity)
BEGIN
RAISERROR ('You cannot place orders when there is no stock for the
order''s product.', 16, 1);
END
ELSE
BEGIN
INSERT INTO Orders (OrderID, Customer, Product, Price, Currency,
Quantity, WithDiscount, Discount, OrderDate, TotalAmount, Dispatched)
SELECT OrderID, Customer, Product, Price, Currency, Quantity,
WithDiscount, Discount, OrderDate, TotalAmount, Dispatched FROM INSERTED;
END;

---- Database auditing

-- Create a new trigger


CREATE TRIGGER DatabaseAudit
-- Attach the trigger at the database level
ON DATABASE
-- Fire the trigger for all tables/ views events
FOR DDL_TABLE_VIEW_EVENTS
AS
INSERT INTO DatabaseAudit (EventType, DatabaseName, SchemaName, Object,
ObjectType, UserAccount, Query, EventTime)
SELECT EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(50)') AS
EventType
,EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]',
'NVARCHAR(50)') AS DatabaseName
,EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(50)')
AS SchemaName
,EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]',
'NVARCHAR(100)') AS Object
,EVENTDATA().value('(/EVENT_INSTANCE/ObjectType)[1]', 'NVARCHAR(50)')
AS ObjectType
,EVENTDATA().value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(100)')
AS UserAccount
,EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]',
'NVARCHAR(MAX)') AS Query
,EVENTDATA().value('(/EVENT_INSTANCE/PostTime)[1]', 'DATETIME') AS
EventTime;

---- Preventing server changes

-- Create a trigger to prevent database deletion


CREATE TRIGGER PreventDatabaseDelete
-- Attach the trigger at the server level
ON ALL SERVER
FOR DROP_DATABASE
AS
PRINT 'You are not allowed to remove existing databases.';
ROLLBACK;

/*
Trigger Optimization and Management
*/

---- Removing unwanted triggers

-- Remove the trigger


DROP TRIGGER PreventNewDiscounts;

-- Remove the database trigger


DROP TRIGGER PreventTableDeletion
ON DATABASE;
-- Remove the server trigger
DROP TRIGGER DisallowLinkedServers
ON ALL SERVER;

---- Modifying a trigger's definition

-- Fix the typo in the trigger message


ALTER TRIGGER PreventDiscountsDelete
ON Discounts
INSTEAD OF DELETE
AS
PRINT 'You are not ALLOWED to remove data from the Discounts table.';

---- Disabling a trigger

-- Pause the trigger execution


DISABLE TRIGGER PreventOrdersUpdate
ON ORDERS;

---- Re-enabling a disabled trigger

-- Resume the trigger execution


ENABLE TRIGGER PreventOrdersUpdate
ON ORDERS;

---- Managing existing triggers

-- Get the disabled triggers


SELECT name,
object_id,
parent_class_desc
FROM sys.triggers
WHERE is_disabled = 1;

-- Check for unchanged server triggers


SELECT *
FROM sys.SERVER_TRIGGERS
WHERE modify_date = create_date;

-- Get the database triggers


SELECT *
FROM sys.TRIGGERS
WHERE parent_class_desc = 'DATABASE';

---- Keeping track of trigger executions

-- Modify the trigger to add new functionality


ALTER TRIGGER PreventOrdersUpdate
ON Orders
-- Prevent any row changes
INSTEAD OF UPDATE
AS
-- Keep history of trigger executions
INSERT INTO TriggerAudit (TriggerName, ExecutionDate)
SELECT 'PreventOrdersUpdate',
GETDATE();

RAISERROR ('Updates on "Orders" table are not permitted.


Place a new order to add new products.', 16, 1);

---- Identifying problematic triggers

-- Get the table ID


SELECT object_id AS TableID
FROM sys.objects
WHERE name = 'Orders';

-- Get the trigger name


SELECT t.name AS TriggerName
FROM sys.objects AS o
-- Join with the triggers table
INNER JOIN sys.triggers AS t ON t.parent_id = o.object_id
WHERE o.name = 'Orders';

SELECT t.name AS TriggerName


FROM sys.objects AS o
INNER JOIN sys.triggers AS t ON t.parent_id = o.object_id
-- Get the trigger events
INNER JOIN sys.trigger_events AS te ON te.object_Id = t.object_Id
WHERE o.name = 'Orders'
-- Filter for triggers reacting to new rows
AND te.type_desc = 'UPDATE';

SELECT t.name AS TriggerName,


OBJECT_DEFINITION(t.object_id) AS TriggerDefinition
FROM sys.objects AS o
INNER JOIN sys.triggers AS t ON t.parent_id = o.object_id
INNER JOIN sys.trigger_events AS te ON te.object_id = t.object_id
WHERE o.name = 'Orders'
AND te.type_desc = 'UPDATE';

You might also like