• Nice article and it made me look back at some stuff I had done a while back and re-jig it to fit in with this.

    I took a slightly different take on generating the mail batches. Also I was not sure why you needed the @query in there twice rather than generating beforehand.

    My other different approach is to curse through each batch and semi-colon separate all the addresses into one addresses line:

    --declare the tables for use

    DECLARE @tempemails TABLE(emailID INT IDENTITY(1,1), emailAddress NVARCHAR(100))

    DECLARE @emailBatches TABLE(batchID INT IDENTITY(1,1), startID INT, endID INT)

    DECLARE @batchSize INT

    DECLARE @totalMails INT

    DECLARE @startID INT

    DECLARE @endID INT

    DECLARE @emailAddresses NVARCHAR(4000)

    DECLARE @firstName NVARCHAR(100)

    DECLARE @mainBody NVARCHAR(MAX)

    SELECT @mainBody = '<p>Welcome to the lovely batch mail we have here</p>

    <p>I think you will like it very much</p>

    <p>Bye then</p>

    <div id="Mysignature">'

    SELECT @batchSize = 8

    --test Data

    INSERT @tempemails(emailAddress)

    SELECT 'fred@fred.com' UNION

    SELECT 'bill@bill.com' UNION

    SELECT 'jane@jane.com' UNION

    SELECT 'norman@norman.com' UNION

    SELECT 'weeble@weeble.com'

    /*

    You might of course do

    INSERT @tempemails(emailAddress)

    SELECT DISTINCT emailAddress

    FROM myTable

    WHERE emailAddress IS NOT NULL

    */

    --see how many mails there are

    SELECT @totalMails = MAX(emailID) FROM @tempemails

    /**

    * compile the list of batches.

    * OK so the endID may go over the MaxID

    * but this with the below cursor this will not matter in the least

    **/

    INSERT @emailBatches(startID, endID)

    SELECT @batchSize*(t1.emailID-1)+1, @batchSize*(t1.emailID-1)+@batchSize

    FROM @tempemails t1

    WHERE @batchSize*(t1.emailID-1)+1 <= @totalMails

    DECLARE mailBatchCursor CURSOR FOR

    SELECT startID, endID

    FROM @emailBatches

    ORDER BY batchID

    OPEN mailBatchCursor

    FETCH NEXT FROM mailBatchCursor

    INTO @startID, @endID

    WHILE (@@fetch_status = 0)

    BEGIN

    --semi-colon separate the email addresses in the batch

    SELECT @emailAddresses = STUFF((SELECT ';' + emailAddress FROM @tempemails WHERE emailID BETWEEN @startID AND @endID FOR XML PATH(''),TYPE).value('.','VARCHAR(MAX)'),1,1,'')

    EXEC msdb.dbo.sp_send_dbmail

    @profile_name = 'Profile Name',

    @recipients=@cs_application_email,

    @blind_copy_recipients =@emailAddresses,

    @subject ='Subject Of The EMail',

    @body = @query,

    @body_format = @mainBody ;

    WAITFOR delay '00:10:00'

    FETCH NEXT FROM mailBatchCursor

    INTO @startID, @endID

    END

    CLOSE mailBatchCursor

    DEALLOCATE mailBatchCursor

    Using FOR XML again you can easily make a table out of any query to add:

    SELECT @table1 = '<h1>Look at the results below</h1><table><tr><th>col1</th><th>col2</th><th>col3</th></tr>' +

    (SELECT '<tr>' + '<td>' + col1 + '</td><td>' + col2 + '</td><td>' + col3 + '</td></tr>' FROM mytable WHERE myid = 5 FOR XML PATH(''),TYPE).value('.','VARCHAR(4000)')

    + '</table>'

    [font="Arial"]_______________________________________________________
    Change is inevitable... Except from a vending machine.[/font]