In this article, I am going to show how we can identify unused SQL databases. In our organization, after release deployment, we receive the database refresh requests. To refresh the database, we were instructed to perform the following tasks:
- Generate a full backup of the source database (UAT/Prod) and restore it on the Development server with a new name
- Delete the old database (previous version) after 15 days of release deployment
Sometimes, the old version of the databases left unattended, and due to that, the disk was getting full very frequently. To handle this issue, we have defined a process to identify the unused databases from SQL Server and email the list to senior team members for approval. To identify the unused databases, we considered two approaches:
- Get the number of the user connection by querying sys. sysprocesses DMV. In this approach, we must create a SQL Server Agent job that queries a DMV and save the query output in the table
- Create a SQL Logon trigger to keep track of the connected users on each database. If any user or application does not access the database, we can drop it
We implemented the first approach to establish the process, but I will explain both approaches in this article.
Use the count of user connection
In this approach, we must create two SQL Server Agent jobs. Every ten minutes, one job will execute and insert the list of users connected to the SQL database in the table. The second job will execute every week and email the list of the databases not used by any application or user.
SQL Job to populate the user connection details
To populate the number of the user connection, we are going to use sys.databases and sys.sysprocesses DMVs. The output of the query will be stored in a table named tblconnectioncount. We will create a SQL Job that executes every 10 mins and inserts the query’s output in a table. The following query populates the Server Name, Database Name, Number of connection, and current date-time of the query execution (getdate()).
1 2 3 4 5 6 7 8 9 |
SELECT @@ServerName [Server Name] ,NAME [Database Name] ,COUNT(STATUS) [Total Number of User connection] ,GETDATE() [Query execution time] FROM sys.databases databases LEFT JOIN sysprocesses processes ON databases.database_id = processes.dbid WHERE database_id <4 GROUP BY NAME Order by COUNT(status) desc |
The following is the output of the query.
We will save the output in the tblconnectioncount table. The following script creates a table.
1 |
create table tblconnectioncount (serverName varchar(500),DBName varchar(1000),TotalConnection int,QueryDate datetime) |
We will create the stored procedure named sp_getconnectioncount to insert the output in the tblconnectioncount table. The code of the stored procedure is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
create procedure sp_getconnectioncount as begin create table tblconnectioncount (serverName varchar(500),DBName varchar(1000),TotalConnection int,QueryDate datetime) Insert into tblconnectioncount SELECT @@ServerName [Server Name] ,NAME [Database Name] ,COUNT(STATUS) [Total Number of User connection] ,GETDATE() [Query execution time] FROM sys.databases databases LEFT JOIN sysprocesses processes ON databases.database_id = processes.dbid WHERE database_id <4 GROUP BY NAME Order by COUNT(status) desc End |
Now, we can schedule the execution of the stored procedure using SQL Server Agent. I have explained how we can create a SQL Server agent job in my previous articles, so I will not include it in this article. The job step should be configured as shown below:
The Job schedule configured, as shown below.
Now, let the SQL Job run for a few days, and based on the output of the following query, you can determine the list of unused SQL databases.
1 |
SELECT DBName,MAX(TotalConnection) AS MAX# FROM tblconnectioncount GROUP BY DBName |
SQL Job to email the unused SQL databases
Now, let us create another job to email the list of the unused databases. As we do not have sufficient data to monitor, we will populate the list of databases that have not been accessed for a few hours. The list of unused databases can be populated by querying the tblconnectioncount table. The following is the query.
1 2 |
SELECT DBName,MAX(TotalConnection) AS TotalConnection FROM tblconnectioncount where QueryDate <GETDATE() and TotalConnection=0 GROUP BY DBName |
To display, the unused databases in HTML table, I have created a stored procedure. The code is following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
alter procedure sp_emailreport as begin declare @UnusedDatabase varchar(max) SELECT @UnusedDatabase = '<table id="AutoNumber1" style="BORDER-COLLAPSE: collapse" borderColor="#111111" height="40" cellSpacing="0" cellPadding="0" width="24%" border="1"> <tr> <td width="27%" bgColor="#D3D3D3" height="15"><b> <font face="Verdana" size="1" color="#FFFFFF">Database Name</font></b></td> <td width="59%" bgColor="#D3D3D3" height="15"><b> <font face="Verdana" size="1" color="#FFFFFF"> Total Connection </font></b></td> </tr> <p style="margin-top: 1; margin-bottom: 0"> </p> <p><font face="Verdana" size="4">Disk Stats</font></p>' SELECT @UnusedDatabase = @UnusedDatabase + '<tr><td><font face="Verdana" size="2">' + convert(varchar,DBName) +'</font></td>' + '<td><font face="Verdana" size="2">' + Convert(varchar,MAX(TotalConnection)) +'</font></td></tr>' FROM tblconnectioncount where QueryDate <GETDATE() and TotalConnection=0 GROUP BY DBName EXEC msdb.dbo.sp_send_dbmail @profile_name = 'YourMailProfile', @recipients='n*******87@outlook.com', @subject = 'List of unused databases', @body = @UnusedDatabase, @body_format = 'HTML' ; End |
Now, as shown above, create a SQL Job. The job step should be configured as shown below:
The Job schedule configured, as shown below:
To test the job, execute the following query:
1 2 3 |
Use msdb Go exec msdb..sp_start_job [DBA - Unused Database Report] |
If the job executes successfully, we will receive an email. Below is the screenshot:
Now, let us understand the second approach.
Use SQL Server Logon trigger
In the second approach, we are going to use the SQL Server logon trigger. This approach will create one SQL job to populate the unused database and email it to the desired recipients. You can read this article to understand more about Logon Triggers.
I have created a table named tblconnection to save the login information generated by the logon trigger. The following code creates the table.
1 2 3 4 5 6 7 8 9 |
CREATE TABLE [dbo].[UserLoginHistory]( [SystemUserName] [varchar](100) NULL, [ServerName] [varchar](100) NULL, [DatabaseUser] [varchar](200) NULL, [SessionID] [int] NULL, [ConnectionTime] [datetime] NULL, [Application] [varchar](500) NULL, [DBName] [varchar](200) NULL ) ON [PRIMARY] |
Whenever a user establishes the connection to the SQL Server instance, the logon trigger inserts the following details in the tblconnection table.
- Login Name
- Hostname
- Username
- Session ID
- Application name
- Database name
I have added the logic to insert the trigger events in the tblconnection table in the Logon trigger code. To create the Logon trigger, execute the following code.
1 2 3 4 5 6 7 8 9 |
CREATE TRIGGER [InsertLoginDetails] ON ALL SERVER FOR LOGON AS BEGIN INSERT INTO [DBA].[dbo].[tblconnection] SELECT ORIGINAL_LOGIN(), HOST_NAME(),USER, @@SPID, GETDATE(), APP_NAME(), ORIGINAL_DB_NAME() WHERE ORIGINAL_LOGIN() <> 'sa' END |
To view the trigger using SQL Server Management Studio (SSMS), expand database instance expand Server Objects Expand Triggers. You can see that the trigger has been created. See the following image:
We want to get the list of SQL databases that have not been used in the last 15 days and email the list to desired recipients. In the query, we have excluded the databases that have been accessed by SQL Server Agent User. The code of the stored procedure is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
use DBA go create procedure sp_email_unused_db_report as begin declare @UnusedDatabase varchar(max) SELECT @UnusedDatabase = '<table id="AutoNumber1" style="BORDER-COLLAPSE: collapse" borderColor="#111111" height="40" cellSpacing="0" cellPadding="0" width="24%" border="1"> <tr> <td width="27%" bgColor="#D3D3D3" height="15"><b> <font face="Verdana" size="1" color="#FFFFFF">Database Name</font></b></td> <td width="59%" bgColor="#D3D3D3" height="15"><b> <font face="Verdana" size="1" color="#FFFFFF"> Database Size in MB</font></b></td> </tr> <p style="margin-top: 1; margin-bottom: 0"> </p> <p><font face="Verdana" size="4">List of databases that has not been used for last 15 days.</font></p>' SELECT @UnusedDatabase = @UnusedDatabase + '<tr><td><font face="Verdana" size="2">' + convert(varchar,a.name) +'</font></td>' + '<td><font face="Verdana" size="2">' + Convert(varchar,sum (size*8)/1024) +'</font></td></tr>' from sys.databases a inner join sys.master_files b on a.database_id=b.database_id where a.name not in (select distinct dbname from dba..tblconnection where Application not like '%SQLAgent%' and SystemUserName not like '%SQLSERVERAGENT%' and ConnectionTime between getdate()-14 and GETDATE()) and a.database_id>5 and a.name not in ('distribution') group by a.name EXEC msdb.dbo.sp_send_dbmail @profile_name = 'YourEmailProfile', @recipients='youremailid@xyz.com', @subject = 'List of unused databases', @body = @UnusedDatabase, @body_format = 'HTML' ; End |
To automate the process, create a SQL Server Agent job. The job step executes the above stored procedure, as shown in the below image.
Configure the schedule as shown below image:
Once the job is created, run the following query to email the list of unused databases.
1 |
Exec msdb..sp_start_job [DBA - Populate Unused Database] |
The email looks like the following image:
Summary
In this article, I have explained two approaches that can be used to identify the unused SQL databases’ list by using the SQL Server logon trigger and by querying the sys.sysprocesses and sys.databases DMVs.
- Different ways to identify and change compatibility levels in SQL Server - July 22, 2024
- Copy SQL Databases between Windows 10 and CentOS using SQL Server data tools - October 19, 2022
- Changing the location of FILESTREAM data files in SQL Database - October 14, 2022