In this post, we’ll discuss how to audit table changes in SQL server using a DDL trigger.
So here’s what we want to do? We want to keep track of all the table changes that are happening across all our databases in a given SQL server instance.
How to keep an history of changes to the database table?
For example, when somebody creates a new table, we want to capture all information. We want to capture the name of the database in which that table is created, the table name itself.
For that purpose, I have created an auditable table which stores the audit information.
Table Sql Script
/****** Object: Table [dbo].[AuditTable] Script Date: 10/21/2020 12:21:58 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[AuditTable]( [DatabaseName] [varchar](300) NULL, [TableName] [varchar](300) NULL, [EventType] [varchar](300) NULL, [LoginName] [varchar](300) NULL, [UserName] [varchar](300) NULL, [SQLCommand] [varchar](5000) NULL, [ChangeDateTime] [datetime] NULL ) ON [PRIMARY] GO
And what’s the eventType, whether it creates table, alter table, or drop table, the login name associated with the person who made that change, the exact transact SQL command that they have executed. And what is the DateTime when that change happened?
In order to achieve this? We’re going to make use of EVENTDATA() function. So what does this EVENTDATA() function? Now, to understand this, let’s take an example.
When we create a table, we know that a DDL event is triggered and that event is created_table.
Now, EVENTDATA() function is going to return us the data associated with that DDL event, and it’s going to return that in XML format. And this is how the data is going to look like.
<EVENT_INSTANCE> <EventType>CREATE_TABLE</EventType> <PostTime>2020-10-21T11:47:48.820</PostTime> <SPID>56</SPID> <ServerName>ADEQUATE-ASHOK\SQLEXPRESS01</ServerName> <LoginName>adk</LoginName> <UserName>dbo</UserName> <DatabaseName>DemoDB</DatabaseName> <SchemaName>dbo</SchemaName> <ObjectName>tbltest</ObjectName> <ObjectType>TABLE</ObjectType> <TSQLCommand> <SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /> <CommandText>create table tbltest(Id int)</CommandText> </TSQLCommand> </EVENT_INSTANCE>
So here I have got a trigger. The name of the trigger is tr_Geteventdataproperties and this is a server scoped Trigger and this will file in response to create table, alter table, and drop table events.
CREATE TRIGGER tr_Geteventdataproperties ON ALL SERVER FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE AS BEGIN select EVENTDATA() END
And if look at what the trigger is doing?
it’s simply selecting whatever this EVENTDATA() function returns. So let’s go ahead and create this trigger.
So now if we create a table, this trigger will fire and it’s going to return the data associated with this create_table event in an example format.
Let’s look at that in action. So here we have create table statement. So when I execute that notice, we get the EVENTDATA() in an XML format.
And when I click on that, notice that we get the event data.
<EVENT_INSTANCE> <EventType>CREATE_TABLE</EventType> <PostTime>2020-10-21T11:47:48.820</PostTime> <SPID>56</SPID> <ServerName>ADEQUATE-ASHOK\SQLEXPRESS01</ServerName> <LoginName>adk</LoginName> <UserName>dbo</UserName> <DatabaseName>DemoDB</DatabaseName> <SchemaName>dbo</SchemaName> <ObjectName>tbltest</ObjectName> <ObjectType>TABLE</ObjectType> <TSQLCommand> <SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /> <CommandText>create table tbltest(Id int)</CommandText> </TSQLCommand> </EVENT_INSTANCE>
So first we have the EventType- create table is the EventType, PostTime- whenever this change happened, the server process ID, the server name, login name, username, database name, schema name, object name that is our table name, object type and the exact transacts SQL command that we have executed.
Now we can use this XML data, reterived, whatever pieces of information that we want and store them in AuditTable.
Now we have to write the trigger to read that XML information and then insert that XML data into our AuditTable table.
CREATE TRIGGER tr_trackchangestoatable ON ALL SERVER FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE AS BEGIN DECLARE @EventData XML SELECT @EventData = EVENTDATA() INSERT INTO DemoDB.dbo.AuditTable (DatabaseName, TableName, EventType, LoginName,UserName, SQLCommand, ChangeDateTime) VALUES ( @EventData.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'varchar(300)'), @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(300)'), @EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(300)'), @EventData.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(300)'), @EventData.value('(/EVENT_INSTANCE/UserName)[1]', 'varchar(300)'), @EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'varchar(5000)'), GetDate() ) END
So tr_trackchangestoatable is the name of the trigger, its server scoped and the trigger is going to respond to three events
And look at what we are doing within the body of the trigger. We are creating a variable of type XML and the name of the variable is @EventData.
And in this variable we are storing whatever data EventData() function is going to return and then we are inserting data into table AuditTable.
Now it’s very important that we give the fully qualified name here because this is a server scoped Trigger.
if you just specify the name of the table, it is not going to find that table. you have to give it the fully qualified name, the database name, schema name, and the actual table name.
And then the columns for that table for which we want to insert the values and notice to retrieve the values from the XML.
We are using the value function and we are specifying the XPath . So if we look at XML data, all event data is present in EVENT_INSTANCE root element.
So here we are saying in a go-to event instance and within that, we have a database name. Retrieve that and the name of the database. So it’s going to retrieve that. And you feel wondering what is this numerical number 1, you can think of this one as select top one in transacts SQL.
So first is going to return the database name and we want that to be converted to that varchar of 300. That’s the data type. And then we are going to, insert that into the AuditTable.
Similarly, we are retrieving the object name. That is the table name in our case. And then then EventType, login name transacts, SQL Command, and the DateTime when that change happened.
So let’s try to create a table, alter this table and And then finally, drop that table.
create table tbldummy(Id int) ALTER TABLE tbldummy ADD Name varchar(255); drop table tbldummy
so now within our AuditTable table. Let’s see if that’s what we have there. So select * from AuditTable and we execute that notice that we have 3 entries and the first entry is create table.
So we have the name of the database, the name of the table, the event type, the login name associated, and the exact transact SQL, the audit DateTime.
where to see the server scoped trigger?
Server scoped trigger is actually created on the server level. So we have the server objects folder. If we expand that, we have got triggers folder and when we expand that, we can find our server scoped trigger there.
The post How to Log record changes in SQL server in an audit table appeared first on Software Development | Programming Tutorials.
Read More Articles
- Linq to SQL Group by and Sum in Select | Linq To SQL Group By and Sum
- How send an HTTP POST request to a server from Excel using VBA?
- What is Difference between where and having clauses in SQL server
- How to Use EXCEPT Operator with an example in Sql
- How to use merge statement in the SQL server
- How to write Re-runnable SQL server query
- How to create Cursor in sql server with example
- How to generate random data in sql server for performance testing
- How to write subquery in select statement in Sql
- How to Log record changes in SQL server in an audit table
- LEFT JOIN with only one match from ordered list
- SQL Server 2008 thousands separator for a column
- SQL column names in Groovy JDBC app?
- Track all logins on to a single SQL Server database
- In T-SQL, does <> '?' mean something other than data not equal to a question mark?
- Django With MSSQL using Pyodbc: Model Forms not being saved
- substring or rtrim
- Does SSRS Report Builder always default date to MM/dd/yyyy?
- Replication with pull subscribers fails under Active Directory policy when using a dedicated replication account
- Creating a Valid SQL Script to Generate a SQL Server Database
- pivot: row in column
- Making a variable that is a list, and using null as an if/else selector
- Mutex Stored Procedure
- Generate SessionID based on given rules using SQL
- What is error of this query ? Error: Incorrect syntax near the keyword 'case'
- how to add new column if it's not exist
- SQL: Using Top 1 in UNION query with Order By
- Find max string value in column
- getting values from rows to columns
- Convert varchar dd/mm/yyyy to dd/mm/yyyy datetime
- Does a WHERE clause that matches a column value to itself get optimized out in SQL Server 2008+?
- Calculate Number of years between dates in Sql
- SQL Server - Join across several tables
- How to check SQL Compact Auto Shrink setting?
- EntityFramework VS SQL Stored PROCEDURE
- XML to SQL Table - Getting Blanks
- Best method to move a large SQL Server table from one database to another?
- SQL Server 2008 query to select data from XML with parameters
- SqlParameter Value or SqlValue?
- How to convert varchar into datetime and skip not valid datetime in TSQL
- Primary key auto-increment manipulation
- What's the cleanest way in SQL to break a table into two tables?
- What program can I use to generate diagrams of SQL view/table structure?
- Populating Menu from a database
- restore attach sql database
- SqlBulkCopy Multiple Tables Insert under single Transaction OR Bulk Insert Operation between Entity Framework and Classic Ado.net
- Distinct query not working fine in sqlserver 2005
- Efficient algorithm for shortest distance between two line segments in 1D
- SQL Procedure to get row number to determine 'Rank'
- SQL MIN Datetime based on first occuranceof a value in another column
- Best way to run a nightly process that manipulates data and sends email via SQL Server 2005
- SQL Server tempdb permanent tables use cases
- Issues with decimal digits in sql table
- Procedure or function expects parameter, which was not supplied
- error in xpstar90.dll at the time of database creation?
- Performing interpolation on a table, grouping by a 3rd field
- SQL Server filter rows which are being selected in other transactions
- Executing Publish Scripts with SQLCMD doesn't return
- Using SQL Server stored procedure to match column name to variable
- What are the non-dev cases of SQL Server User Instances (vs CE and other embedded databases)?
- Copy SQL Server database with PowerShell script
- passing string as datetime parameter in sql command
- What can I do about a SQL Server ghost FK constraint?
- Drop the table Or Delete all data, and how do events based on time on server
- 2 connections to SQL Server
- Stored Procedures - Updating and Inserting
- Select into Temp table cannot exist in seperate IF condition?
- Selecting ONLY Duplicates from a joined tables query
- Joining select on one row for same result id
- Deleting millions on records while using log shipping - SQL Server
- Insert data from DataTable to database table
- Getting sp value from another sp
- sql server - how do i find rows with whitespace in a column
- Microsoft Sql Server to MySQL synchronization through php
- SQL Query to Return Count Based on Time
- How to select different columns depending on values in a column
- How to combine First, Middle, Last names in a single statement?
- Show all and only rows in table 1 not in table 2 (using multiple columns)
- Is there a way to have a SqlConnection that will write while an associated DataReader is open?
- Why PrimaryKey NonClustered Index and Separate Clustered Index on Same PrimaryKey Columns are Designed.?
- Two SQL Server instances with the same name?
- T-SQL Calculate the max value from an Alpha Numeric key
- Check if date is between the start of May this year and the end of May next year
- migration view between Mssql Server to MySql
- SQL Server 2008 R2: Select statement within REPLACE()
- How do I return values from my stored procedure from SQL to c#
- Outer join on two tables with sequential guid stalls
- Retrieve column names of a table into a combo box
- Anonymous access to azure table
- Return Query Based on Tree Structure
- ALL vs ANY evaluation in SQL Server
- How to migrate data from MongoDB to SQL-Server?
- Synchronising databases
- Case insensitive schema object names with case sensitive comparisons
- T-SQL split result into two rows
- T-SQL try ... catch and multiple batches (2 begin...end, if...else)
- SQL join following foreign key: statically check that LHS is key-preserved
- Can you use an asterisk (*) to filter a column in a WHERE clause under SQL Server 2008?
- How to extract data from this table and convert it into this specific format using t-sql?
- How do you implement Data Quality & Validation rules in a data warehouse?
- SQL Server select duplicated rows
- help me generate sample data using sql script
- Sql Server Management Studio 2008 not scripting table permissions
- Drop row in Pandas dataframe if matching multiple values
- How do I perform nested joins using Laravel's query builder?
- VBA clear just pivot table cache, but leaving pivot table structure
- Would building an application using a Sql Server Database File (mdf) be a terrible idea?
- C# select from SQL Server database
- Finding Rows containing % symbol in a table
- sql select based off multiple values
- Have all Records in one Field
- Coalesce Columns in SQL Server
- SQL performance - How to speed up count distincts?
- Find the sum of profit since last transaction(same day or a previous day) at the end of a day in T-SQL
- Why should I use stored procedures to perform INSERT, UPDATE and DELETE operations?
- SQL Select only records with no duplicates
- PDO insert into MSSQL datetime swaps day and month
- From a table with 2 columns (key and value), how to return a json with values of the column?
- Should I create separate SQL Server database for each user?
- Where clause to filter timestamp data by using only date
- Count group by date zero result
- How to get next value of SQL Server sequence in Entity Framework?
- How to use the divide function in the query?
- Failed to restore Team Foundation server databases
- Determining index of last uppercase letter in column value (SQL)?
- Cannot insert NULL values into column 'USERNAME', table 'tempdb.dbo.#temptable error
- Automate the SQL Server backup in C#
- sort binary datetime
- C# class object error as unassigned local Variable
- Order query result based off of information in another table
- Sql Server In memory OLTP table for System Versioned Tables
- Extracting Date from Text in SQL using cursor
- What is the PostgreSQL equivalent for ISNULL()
- Auto Incremented Value Selection
- How to enforce a CHECK constraint across multiple tables
- Can't connect from the Docker container with ASP.NET Core to SQL Server container
- SQL Sort by based off of CASE - Not working
- SQL Server Unique Identitifier vs Integer
- Creating a clustered index on a foreign key is frequently joined to another table
- SQL Query Cleanup using COUNT
- I have an IQueryable with WHERE IN (ids). Can ToList() return the same order as ids?
- Get column value as array list for distinct attribute
- Text in PDF render of Reporting Services report "overprints"
- Select the oldest date for a row and create a new column with values based on the oldest date or other
- SQL server connection issue using Spring
- Matching multiple key/value pairs in SQL
- "must declare the scalar variable '@x'" when binding SQL parameters
- Save executed Sql inside table
- Common syntax in MySQL and MSSQL for IF INSERT ELSE UPDATE
- best way to grant read only access to 2 tables in SQL Server 2005?
- How to format select query output
- Use of AcceptChanges with SQL Server
- Load SQL query result data into cache in advance
- Limit results in SQL
- SQL server select list of rows with same last action
- EXEC an INSERT with a dynamic Schema TSQL SSMS
- conditions on count in a select
- SQL Server 2012 CREATE FUNCTION compile error
- How do I Switch single quotes to double single quotes on output from T-SQL SELECT statement
- SSRS BIDS expression Show/Hide image based on passed through parameter