z#RB5E2utU3K6rft$#J5CaQpKxDW&@!S

This should be the common issue faced by many of us where the database mail is used to send bulk mails on a regular basis. We have a database server which is specifically used for sending weekly newsletters to all the subscribed users. The number of subscriptions increased over a period of time and so as the number of newsletters. One day the disk drive, holding the system databases, started alerting for the low disk space. On investigation, I found that the msdb database has grown to 7 GB in size!

         As we know that msdb maintains various types of history information like backup history, jobs history, sent mails and attachments history etc. Now the question is to find out the table(s) holding the largest data. I have found the following query from the Net which helped me to find out the name of the tables in descending order of…

View original post 591 more words

SQL Server: Controlling the growth of msdb database caused by Database Mail


This should be the common issue faced by many of us where the database mail is used to send bulk mails on a regular basis. We have a database server which is specifically used for sending weekly newsletters to all the subscribed users. The number of subscriptions increased over a period of time and so as the number of newsletters. One day the disk drive, holding the system databases, started alerting for the low disk space. On investigation, I found that the msdb database has grown to 7 GB in size!

         As we know that msdb maintains various types of history information like backup history, jobs history, sent mails and attachments history etc. Now the question is to find out the table(s) holding the largest data. I have found the following query from the Net which helped me to find out the name of the tables in descending order of space consumed on the disk.

(As the above issue had already been fixed, I can not show you the 7 GB grown msdb. For this demo I am running this query on a server having 1.5 GB grown msdb)

use msdb
 
go
 
sp_helpdb msdb

msdb database report

01 --Query 1 - Checking the size of tables in a database
02 select
03     object_name(i.object_id) as ObjectName,
04     i.[name] as IndexName,
05     sum(a.total_pages) as TotalPages,
06     sum(a.used_pages) as UsedPages,
07     sum(a.data_pages) as DataPages,
08     (sum(a.total_pages) * 8) / 1024 as TotalSpaceMB,
09     (sum(a.used_pages) * 8) / 1024 as UsedSpaceMB,
10     (sum(a.data_pages) * 8) / 1024 as DataSpaceMB
11 from
12     sys.indexes i
13     inner join sys.partitions p
14         on i.object_id = p.object_id and i.index_id = p.index_id
15     inner join sys.allocation_units a
16         on p.partition_id = a.container_id
17 group by
18     i.object_id,
19     i.index_id,
20     i.[name]
21 order by
22     sum(a.total_pages) desc,
23     object_name(i.object_id)
24 go

Checking the size of tables in a database

          We can see that sysmail_mailitems table is occupying most of the space on the disk, around 1.3 GB spanning across 171812 pages. This table stores the database mail messages which are sent, unsent, retrying, and failed. Microsoft recommends maintaining this table by periodically deleting the messages based on the organizations document retention program. The sysmail_delete_mailitems_sp procedurelocatedin the msdb database permanently deletes e-mail messages from this table. The syntax of the procedure is shown below

1 sysmail_delete_mailitems_sp 
2     [ [ @sent_before = ] 'sent_before' ]
3     [ , [ @sent_status = ] 'sent_status' ]

          The date option allows deleting e-mails before the specified date and the status option allows deleting e-mails of a particular type i.e. sent, unsent, retrying, and failed.

          As deleting a huge amount of records in one shot takes a good amount of time we can purge the data in small batches. The below query will identify since when the data is lying in the table. This is especially useful when the data is piled up over a long period of time.

01 --Query 2 - Checking the records by year, month
02 select
03     datepart(yy,sent_date) [Year],
04     datepart(mm,sent_date) [Month],
05     count(*) 'NoOfRows'
06 from
07     sysmail_mailitems
08 group by
09     datepart(yy,sent_date),
10     datepart(mm,sent_date)
11 order by
12     1,2

Checking the records by year and month  

        The table has data for two months. Let’s keep the data for March and deleted the February data. The following query will do the job,

1 --Query 3 – Purging the data before 1st March 2011
2 DECLARE @DelDate nvarchar(20) ;
3 SET @DelDate = '03/01/2011'
4 EXECUTE msdb.dbo.sysmail_delete_mailitems_sp @sent_before = @DelDate;

Purging the data

           Now run the Query 1 and check the output. The total table space which was 1372 MB has come down to 623 MB and also the Total Pages from 171812 to 79836.

 Checking the size of tables in a database

          Let’s check the database size using,

 sp_helpdb msdb

msdb database reporting

          No luck???!!! In fact the log size has increased. Don’t worry, now we need to truncate the log and shrink the database so that the free space will be release to the operating system. Run the following code and recheck the database size.

1 --Query 4 - Truncate the log and shrink the database
2 checkpoint
3  
4 backup log msdb with truncate_only
5  
6 dbcc shrinkdatabase ('msdb')
7  
8 sp_helpdb msdb

 msdb database report

 It worked…Smile

How To Send Mail From Database In Tabular Form


Here is the sample code.

CREATE TABLE #Temp 
( 
  [Rank]  [int],
  [Player Name]  [varchar](128),
  [Ranking Points] [int],
  [Country]  [varchar](128)
)

INSERT INTO #Temp SELECT 1,'Rafael Nadal',12390,'Spain' UNION ALL SELECT 2,'Roger Federer',7965,'Switzerland' UNION ALL SELECT 3,'Novak Djokovic',7880,'Serbia'
DECLARE @xml NVARCHAR(MAX) DECLARE @body NVARCHAR(MAX)
SET @xml = CAST(( SELECT [Rank] AS 'td','',[Player Name] AS 'td','', [Ranking Points] AS 'td','', Country AS 'td' FROM #Temp ORDER BY Rank FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET @body ='<html><body><H3>Tennis Rankings Info</H3> <table border = 1> <tr> <th> Rank </th> <th> Player Name </th> <th> Ranking Points </th> <th> Country </th></tr>'
SET @body = @body + @xml +'</table></body></html>'
EXEC msdb.dbo.sp_send_dbmail @profile_name = 'SQL ALERTING', -- replace with your SQL Database Mail Profile @body = @body, @body_format ='HTML', @recipients = 'bruhaspathy@hotmail.com', -- replace with your email address @subject = 'E-mail in Tabular Format' ;
DROP TABLE #Temp

The HTML output from the above example looks like this:

<html>
<body>
<h3>Tennis Rankings Info</h3>
<table border="1">
 <tr>
  <th>Rank </th>
  <th>Player Name </th>
  <th>Ranking Points </th>
  <th>Country </th>
 </tr>
 <tr>
  <td>1</td>
  <td>Rafael Nadal</td>
  <td>12390</td>
  <td>Spain</td>
 </tr>
 <tr>
  <td>2</td>
  <td>Roger Federer</td>
  <td>7965</td>
  <td>Switzerland</td>
 </tr>
 <tr>
  <td>3</td>
  <td>Novak Djokovic</td>
  <td>7880</td>
  <td>Serbia</td>
 </tr>
</table>
</body>
</html>

And this is how the email would look:

how to send an email from sql server with data in a tabular format

As another example, if you want to change this and select FirstName, LastName and EmailAddress from Person.Contact in the AdventureWorks database and order it by LastName, FirstName you would make these changes:

DECLARE @xml NVARCHAR(MAX)
DECLARE @body NVARCHAR(MAX)

SET @xml = CAST(( SELECT [FirstName] AS 'td','',[LastName] AS 'td','', [EmailAddress] AS 'td' FROM Person.Contact ORDER BY LastName, FirstName FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET @body ='<html><body><H3>Contact Info</H3> <table border = 1> <tr> <th> First Name </th> <th> Last Name </th> <th> Email </th></tr>'
SET @body = @body + @xml +'</table></body></html>'
EXEC msdb.dbo.sp_send_dbmail @profile_name = 'SQL ALERTING', -- replace with your SQL Database Mail Profile @body = @body, @body_format ='HTML', @recipients = 'bruhaspathy@hotmail.com', -- replace with your email address @subject = 'E-mail in Tabular Format' ;