New or Changed Table Alert with DDL Triggers

By Lori Brown | Helpful Scripts

Sep 24

— By Lori Brown  @SQLSupahStah

If you are the usual DBA, you are often the last person to know when something new has been added to the databases you are managing. After finding that some people in an organization were abusing their access to a certain database, I decided to create a TSQL DDL trigger at the database level to alert me when tables are created or changed. This DDL trigger could also be created at the server level meaning that it would alert for the same events no matter what database the event happens in. An alternative to an immediate alert is to log the data captured from the alerts to a table in an administrative database and then possibly reporting on that later on.

For the notification that was immediately needed a simple e-mail alert was sufficient so I made it into an easily readable html format. Once in place, you should receive an e-mail for each new object created or altered. There are a lot of events you can set up DDL triggers on. A full list of them can be found on MSDN at: https://msdn.microsoft.com/en-us/library/bb522542.aspx

In my code below I have used an administrative database named ADMIN to set up the DDL trigger on and to create and alter tables on to demonstrate that the e-mail alert works. Of course, you will need to have Database Mail enabled and working and you will need to know a mail profile to use as well.

USE ADMIN

GO

CREATE TRIGGER NewChangedTblAlert

ON DATABASE

FOR CREATE_TABLE, ALTER_TABLE

AS

BEGIN

SET NOCOUNT ON

DECLARE @emailbody NVARCHAR(MAX)

DECLARE @emailsubj NVARCHAR(100)

DECLARE @EventDate DATETIME

DECLARE @EventType NVARCHAR(100)

DECLARE @EventDDL NVARCHAR(MAX)

DECLARE @EventXML XML

DECLARE @DatabaseName NVARCHAR(255)

DECLARE @SchemaName NVARCHAR(255)

DECLARE @ObjectName NVARCHAR(255)

DECLARE @HostName VARCHAR(64)

DECLARE @IPAddress VARCHAR(32)

DECLARE @ProgramName NVARCHAR(255)

DECLARE @LoginName NVARCHAR(255)

SET @EventXML = EVENTDATA()

SET @IPAddress = (SELECT client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID)

SELECT @EventDate = GETDATE(),

@EventType = @EventXML.value(‘(/EVENT_INSTANCE/EventType)[1]’, ‘NVARCHAR(100)’),

@EventDDL = @EventXML.value(‘(/EVENT_INSTANCE/TSQLCommand)[1]’, ‘NVARCHAR(MAX)’),

@DatabaseName = DB_NAME(),

@SchemaName = @EventXML.value(‘(/EVENT_INSTANCE/SchemaName)[1]’, ‘NVARCHAR(255)’),

@ObjectName = @EventXML.value(‘(/EVENT_INSTANCE/ObjectName)[1]’, ‘NVARCHAR(255)’),

@HostName = HOST_NAME(),

@ProgramName = PROGRAM_NAME(),

@LoginName = SUSER_SNAME()

— Build e-mail body

SET @emailbody=‘<p> A table has been created or altered in the ‘+@DatabaseName+‘ database.</p>’

— add table attributes

SET @emailbody=@emailbody+‘<table border=”2″ cellspacing=”2″ cellpadding=”2″>

<tbody align=”left” style=”font-family:Arial; font-weight:700; font-size: 12; color: red;”

<TR><TD colspan=9 >’

— add table title

SET @emailbody=@emailbody+‘New or Changed Table </TD></TR></tbody>’

— add column headers

SET @emailbody=@emailbody+ ‘<tbody align=”left” style=”font-family:Arial; font-size: 11;”

<TR><TH>DDL Type</TH><TH>DDL Statement</TH>

<TH>Schema</TH><TH>DatabaseName</TH><TH>TableName</TH><TH>LoginName</TH>

<TH>HostName</TH><TH>IPAddress</TH><TH>ProgramName</TH></TR></tbody>’

— add data

SELECT @emailbody=@emailbody +‘<tbody align=”left” style=”font-family:Arial; font-size: 11;”

<TR><TD>’+@EventType+‘</TD><TD>’+@EventDDL+‘</TD><TD>’+

@SchemaName+‘</TD><TD>’+@DatabaseName+‘</TD><TD>’+@ObjectName+

‘</TD><TD>’+@LoginName+‘</TD><TD>’+@HostName+‘</TD><TD>’+

@IPAddress+‘</TD><TD>’+@ProgramName+‘</TD></TR></tbody>’

— subject

SET @emailsubj = ‘New or Changed Tables in ‘+@DatabaseName+‘ Database’

— Send an html formatted e-mail to notify ddl events

EXEC msdb.dbo.sp_send_dbmail

@profile_name = ‘DBMailProfile’, — Use valid database mail profile here

@recipients = ‘somebody@company.com’, — Use valid email address here

@subject = @emailsubj,

@body = @emailbody,

@body_format = ‘HTML’

SET NOCOUNT OFF

END

GO

To see a list of DDL triggers, you can query the sys.triggers table.

SELECT * FROM sys.triggers

Blog_20150924_1

Now we need to test that an alert is sent by creating and altering tables.

USE ADMIN

GO

—  create 2 tables, should send e-mail for each

CREATE TABLE Table1

(

COL1 INT,

COL2 VARCHAR(2)

)

GO

CREATE TABLE Table2

(

COL1 INT,

COL2 VARCHAR(2)

)

GO

–Generate an alter table event, should send e-mail

ALTER TABLE Table1

ADD COL3 VARCHAR(6)

GO

Here is an example of the e-mail alert that is sent.

Blog_20150924_2

Once again, it would be really easy to log events to a table. Just create a table to hold the events. You can even create this in a different database than the one that has the DDL trigger created on it:

USE MonitorDB

GO

CREATE TABLE dbo.DDLEvents

(       ID          INT IDENTITY(1,1) NOT NULL,

EventDate    DATETIME NOT NULL,

EventType    NVARCHAR(64),

EventDDL     NVARCHAR(MAX),

EventXML     XML,

DatabaseName NVARCHAR(255),

SchemaName   NVARCHAR(255),

ObjectName   NVARCHAR(255),

HostName     VARCHAR(64),

IPAddress    VARCHAR(32),

ProgramName  NVARCHAR(255),

LoginName    NVARCHAR(255)

)

Then add this insert statement to the trigger after all the info for the variables has been gathered. That would be either just before creating the e-mail body or maybe after sending the e-mail alert.

INSERT INTO MonitorDB.dbo.DDLEvents (

EventDate,

EventType,

EventDDL,

EventXML,

DatabaseName,

SchemaName,

ObjectName,

HostName,

IPAddress,

ProgramName,

LoginName

)

VALUES (

@EventDate,

@EventType,

@EventDDL,

@EventXML,

@DatabaseName,

@SchemaName,

@ObjectName,

@HostName,

@IPAddress,

@ProgramName,

@LoginName

)

Now you can report on the types of change activity that you log. I’ll leave it to you to play with logging and reporting. Have fun with it!

I always like to provide some form of cleanup should you need it. You can either disable or drop the DDL trigger by using the statements below:

— Disable trigger

USE ADMIN

GO

DISABLE TRIGGER NewChangedTblAlert ON DATABASE

GO

— Drop trigger

USE ADMIN

GO

DROP TRIGGER NewChangedTblAlert ON DATABASE

GO

If you need to implement DDL triggers, I hope this has been helpful. For more information about blog posts, concepts and definitions, further explanations, or questions you may have…please contact us at SQLRX@sqlrx.com. We will be happy to help! Leave a comment and feel free to track back to us. We love to talk tech with anyone in our SQL family!

Follow

About the Author

Lori is an avid runner, cross fitter and SQL enthusiast. She has been working for SQLRX for 15 years and has been working with SQL in general for 25 years. Yup...she is an old hand at this stuff.

>