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.
Download as TXT, PDF, TXT or read online on Scribd
0 ratings0% 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.
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;
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';