Sending Database emails to multiple recipients while tweaking the body - sql-server

i need to send emails to approx, twenty recipients each day, i build a temp table with two columns
Email_Body
Email_address
there are at max 50 rows in the table each day
i want to loop though this table and execute sp_send_email
EXEC msdb.dbo.sp_send_dbmail
#Profile_name = 'DBA',
#recipients = #email_address,
#body = #Email_Body,
#subject = 'Test Email'
is there a way to do this without a cursor?
any links to an example would be appreciated, i have searched and can't find such an example. i am sure it is a very common process.

You might try the solution found here.

I think this code sample would help:
while (#count <=(select COUNT(*) from #table))
begin
select top 1 #Recepient_Email=Emp_Email,#mailBody=body from #table where ID=#count
EXEC msdb.dbo.sp_send_dbmail
#profile_name='Profile1',
#recipients=#Recepient_Email,
#subject = 'This is subject of test Email'
#body = #mailbody,
#body_format = 'HTML'
set #count =#count +1
END

--Email Campaign.
declare #HtmlModel varchar(max)
declare #HtmlBody varchar(max)
SELECT #HtmlModel = EmailBody
FROM EmailCampaigns
WHERE ID = 1
--a different process will have created an awesome looking well formed html with our known
--placeholders for [FirstName] [LastName] [Phone] [Email] that might ber substituted for individualization
--PRINT #HtmlModel;
SET #HtmlBody=''
declare
#mailID int,
#id int,
#first varchar(64),
#last varchar(64),
#phone varchar(64),
#email varchar(64)
declare c1 cursor for
--################################
SELECT ID,
ISNULL(FirstName,'Friend') AS FirstName,
ISNULL(LastName,'') AS LastName,
ISNULL(Phone,''),
ISNULL(Email ,'')
--the row_number is so that if i put your name in the database five times, you only get a single email.
FROM (
select ROW_NUMBER() over (partition by email order by email,len(firstname)desc,len(lastname)desc ) AS RW, *
from RawContacts
where email <> '') x
WHERE RW = 1
--this WHERe stays in place until we are ready to go LIVE with the email.
and email IN('lowell#somedomain.com','otherReviewer#somedomain.com')
--################################
open c1
fetch next from c1 into #id,#first,#last,#phone,#email
While ##fetch_status <> -1
begin
SET #HtmlBody = REPLACE(#HTMLModel,'[FirstName]',#first)
SET #HtmlBody = REPLACE(#HtmlBody,'[LastName]', #last)
SET #HtmlBody = REPLACE(#HtmlBody,'[Phone]', #phone)
SET #HtmlBody = REPLACE(#HtmlBody,'[Email]', #email)
--EXEC msdb.dbo.sp_send_dbmail
#Profile_name = 'Database Mail Profile Name',
#recipients=#email,
#subject = 'Our non profits Call for Volunteers',
#body = #HtmlBody,
#body_format = 'HTML',
#mailitem_id = #mailID OUTPUT
--#body_format = 'TEXT'
INSERT INTO CampaignRecipients(Campaign,RawContactID,MailSentID,MailSentDate)
SELECT 1,#id,#mailID,GETDATE()
fetch next from c1 into #id,#first,#last,#phone,#email
end
close c1
deallocate c1
GO

Related

SQL Server - adding nvarchar variable to email with html formatting

I just can't see what I'm doing wrong but it's probably a simple issue.
I use sp_send_dbmail to send an email notification to users. The body of the email is formatted with html.
When I do tests, the body is blank. I've narrowed it down to the #Name variable. If I add a simple string like 'John' then the body is not blank, but I'm doing something wrong with the #Name variable.
I would think the CAST of #Name is unnecessary, but I've just been trying different things to no avail.
How do I incorporate #Name in a correct way?
Below is the code with parts of the irrelevant html left out to shorten it:
Alter Proc [dbo].[SendPayslipEmail]
(#TVP EmailAddressTableType READONLY,
#Period NVARCHAR(50),
#Message NVARCHAR(MAX))
as
begin
DECLARE #ResTag INT
DECLARE #Email_Address VARCHAR(150)
DECLARE #emailHTML NVARCHAR(MAX) ;
DECLARE #Subject NVARCHAR(50);
DECLARE #Name NVARCHAR(50);
DECLARE #LoginURL NVARCHAR(50);
SET #Subject = (Select [Value] from [CustomerEmailSettings] where [Setting] = 'Payslip Email Subject')
SET #LoginURL = (Select [Value] from [CustomerEmailSettings] where [Setting] = 'LoginURL')
DECLARE cursorSendEmail CURSOR
FOR SELECT [Resource Tag],[E-mail Address] FROM #TVP;
OPEN cursorSendEmail
FETCH NEXT FROM cursorSendEmail INTO #ResTag,#Email_Address;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Name = CAST((SELECT [Display Name] FROM DisplayName(#ResTag)) AS NVARCHAR(50))
SET #emailHTML =
N'<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">'
+N'<head>'
<!-- More html -->
+N'<p class=MsoNormal><b><span style=''color:#4485B8''>Dear '
+#Name
+N',<o:p></o:p></span></b></p>'
<!-- More html -->
+N'</html>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'PayslipEmails',
#recipients = #Email_Address,
#body = #emailHTML,
#body_format = 'HTML',
#subject = #Subject;
FETCH NEXT FROM cursorSendEmail INTO #ResTag,#Email_Address;
END
CLOSE cursorSendEmail
DEALLOCATE cursorSendEmail
end

How to make an automatic reminder with jobs with TSQL?

Is there a way to make an automatic reminder via email when those dates comes and send the rows info from the same table, for example:
In the following table we have to columns with different dates, DUEDATE need to be a reminder from expedition and EXPDV is another reminder 15 day before DUEDATE, So when one of those dates from any row comes I want to make to send and automatic Reminder via email with the row info (PSLOT,PSQRCV,ETC) From that table , any ideas?
USE [database_name]
GO
SET NOCOUNT ON;
DECLARE #count INT =
(
SELECT COUNT(*) FROM dbo.TableName
WHERE DATEDIFF(DAY, EXPDV, DUEDATE) = 15
)
IF #count > 0
BEGIN
DECLARE #html NVARCHAR(max)
, #subject NVARCHAR(max) = 'Your email subject here'
, #email NVARCHAR(255)
, #PSLOT NVARCHAR(255)
, #PSQRCV NVARCHAR(255)
, #ETC NVARCHAR(255)
DECLARE send_remider CURSOR FOR
SELECT email, PSLOT,PSQRCV,ETC FROM dbo.TableName
WHERE DATEDIFF(DAY, EXPDV, DUEDATE) = 15
OPEN send_remider
FETCH NEXT FROM send_remider INTO #email, #PSLOT, #PSLOT, #ETC
WHILE ##FETCH_STATUS = 0
BEGIN
SET #html = 'Your message body text here....' + #PSLOT, #PSQRCV, #ETC
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'profile_name'
, #recipients = #email
, #subject = #subject
, #body = #html
, #body_format = 'HTML'
FETCH NEXT FROM send_remider INTO #email, #PSLOT, #PSLOT, #ETC
END
CLOSE send_remider
DEALLOCATE send_remider
GO
You can create a job and run this cursor. I haven't tested it. So, you should make yourself a recipient during testing.

How to call sdb.dbo.sp_send_dbmail for each row of a sql result set

I have a SQL Server table called EMails which consists of the columns
Id int,
Subject nvarchar(100)
Body nvarchar(MAX),
Recipient nvarchar(100)
IsSent bit
My application populates it.
I want to use SQL Server to send the email.
I know how to send an email with
EXEC msdb.dbo.sp_send_dbmail
However the query
SELECT Subject,Body,Recipient FROM EMails
WHERE IsSent=0
can return up to multiple
I also want to SetIsSent to 1 after the mail has been sent.
Question:
Is it possible using a scheduled SQL server job. If so can you provide a code example?
Is it recommended for sending up to 50 emails max when the query is put in a sql server job that runs every 15 minutes
DECLARE #Subject NVARCHAR(100),
#Body NVARCHAR(MAX),
#Recipient NVARCHAR(100),
#ID INT,
#MailID INT
DECLARE #mails TABLE (ID INT, MailID INT)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY LOCAL FOR
SELECT ID, [Subject], Body, Recipient
FROM dbo.EMails
WHERE IsSent = 0
OPEN cur
FETCH NEXT FROM cur INTO #ID, #Subject, #Body, #Recipient
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = '....',
#recipients = #Recipient,
#subject = #Subject,
#body = #Body,
#mailitem_id = #MailID OUTPUT
INSERT INTO #mails (ID, MailID)
VALUES (#ID, #MailID)
FETCH NEXT FROM cur INTO #ID, #Subject, #Body, #Recipient
END
CLOSE cur
DEALLOCATE cur
WAITFOR DELAY '00:00:01'
UPDATE u
SET IsSent = 1
FROM dbo.EMails u
JOIN #mails m ON u.ID = m.ID
JOIN msdb.dbo.sysmail_mailitems s ON m.MailID = s.mailitem_id
WHERE s.sent_status = 1
-- failed mails
SELECT s.recipients, s.[subject], s.body, s.sent_status
FROM #mails m
JOIN msdb.dbo.sysmail_mailitems s ON m.MailID = s.mailitem_id
WHERE s.sent_status != 1

Using a cursor in a trigger to change subject of db_sendmail

SQl Server 2008 R2 - I have a trigger that's set up to send an email to a specific email address that is monitored by a third party system which sends out messages to certain groups of people based upon the subject of that message. The limitation to this is that only one group number can be in the subject per message. But I need to send the same message from the trigger to at least two groups meaning that two separate emails must be generated with two different subject lines. I'm thinking possibly a cursor in the trigger would accomplish this but I'm not sure how to write it.
So in the code below I have #cat in which I need to send an email with the subject 'ED' EVERY time the trigger kicks off. I then need to send another email with the subject being set based on the condition of the case statement currently used to set the subject line. I've updated the code as follows and by removing the scalar variables have moved to a set based trigger(??) I've also created and inserted the information into an "email" table that will have a service set up to run every 10 sec and email all records that are set as 0 and then update the flag to 1.
New Code -
CREATE TABLE [dbo].[tb_BatchEmail] (
[BatchEmailID] [bit] NOT NULL DEFAULT 0
,[To] [varchar](50) NOT NULL DEFAULT 'someemail' --will never change
,[Body] [varchar](255) NULL
,[Subject] [varchar](20) NOT NULL
,[Profile] [varchar](50) NOT NULL DEFAULT 'Alert' --will never change
,[OrderID] [varchar] (25) NULL
,[OrderDateTime] [datetime] NULL
,[SentDateTime] [datetime] NULL
,CONSTRAINT msg_pk PRIMARY KEY CLUSTERED (BatchEmailID)
) ON [PRIMARY]
GO
ALTER TRIGGER [dbo].[VeOrders] ON [dbo].[Orders]
FOR INSERT
AS
SELECT #visitid = i.VisitID
,#priority = Priority
,#cat = Category
,#procedure = OrderedProcedureName
,#orderid = OrderID
,#orderdate = OrderDateTime
,#locationid = CurrentLocationID
,#roomid = CASE
WHEN RoomTreatmentID IS NULL
THEN 'No Room#'
ELSE RoomTreatmentID
END
FROM inserted i
INNER JOIN livedb.dbo.EdmPatients edp
ON edp.VisitID = i.VisitID
WHERE Priority = 'STAT'
AND Category IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
AND CurrentLocationID = 'ED'
IF #cat IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
AND #priority = 'STAT'
AND #locationid = 'ED'
BEGIN
DECLARE #msg VARCHAR(500)
DECLARE #subject VARCHAR(500)
SET #msg = #roomid + '-' + #procedure + '-' + #priority + '.' --+ 'Order DateTime/Number ' + #order + '/+ #locationid + ' ' + #orderid
SET #subject = CASE
WHEN #cat = 'US'
THEN 'Test Ultra Sound'
WHEN #cat = 'CT'
THEN 'Test C T'
WHEN #cat = 'XRAY'
THEN 'Test X-Ray'
END
INSERT INTO livedb.dbo.tb_BatchEmail (
Body
,[Subject]
,OrderID
,OrderDateTime
)
SELECT Body = #msg
,[Subject] = #subject
,OrderID = #orderid
,OrderDateTime = #orderdate
END
Old Code -
ALTER TRIGGER [dbo].[VeOrders] ON [dbo].[Orders]
FOR INSERT
AS
IF NOT EXISTS (
SELECT *
FROM dbo.EdmPatients
WHERE CurrentLocationID = 'ED'
)
RETURN
DECLARE #priority VARCHAR(50)
DECLARE #cat VARCHAR(50)
DECLARE #procedure VARCHAR(50)
DECLARE #orderid VARCHAR(50)
DECLARE #locationid VARCHAR(10)
DECLARE #roomid VARCHAR(10)
DECLARE #visitid VARCHAR(50)
SELECT #visitid = VisitID
,#priority = Priority
,#cat = Category
,#procedure = OrderedProcedureName
,#orderid = OrderID
,#locationid = CurrentLocationID
,#roomid = CASE
WHEN RoomTreatmentID IS NULL
THEN 'No Room#'
ELSE RoomTreatmentID
END
FROM inserted i
INNER JOIN dbo.EdmPatients edp
ON edp.VisitID = i.VisitID
WHERE Priority = 'STAT'
AND Category IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
AND CurrentLocationID = 'ED'
IF #cat IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
AND #priority = 'STAT'
AND #locationid = 'ED'
BEGIN
DECLARE #msg VARCHAR(500)
DECLARE #subject VARCHAR(500)
SET #msg = #roomid + '-' + #procedure + '-' + #priority + '.'
SET #subject = CASE
WHEN #cat = 'CT'
THEN '55194'
WHEN #cat = 'US'
THEN '59843'
WHEN #cat = 'XRAY'
THEN '70071'
END
EXEC msdb.dbo.sp_send_dbmail
#recipients = N'someemail'
,#body = #msg
,#subject = #subject
,#profile_name = 'Alerts'
END
I would separate out the concerns from creating the emails and sending them. Have a service whose only job is to send emails. Emails are abstract in that they all have a From, a To, a Body, and a Subject. Create a table (named BatchEmail for example) that holds all of those fields.
Then your trigger is simple. It just inserts into your BatchEmail table in a normal select statement.
insert into BatchEmail
(To, Body, Subject, Profile)
select N'someemail',
#msg,
CASE
WHEN #cat = 'CT'
THEN '55194'
WHEN #cat = 'US'
THEN '59843'
WHEN #cat = 'XRAY'
THEN '70071'
END,
'Alerts'
Your BatchEmail table should have some column or flag indicating when or if it was sent.
Your service (or even the Insert Trigger on the table, if you really love triggers) Could just select 1 row at a time that hasn't been processed.
while true
begin
select top 1
#BatchEmailID = batchEmailID,
#To = to,
#From = from,
#Body = body,
#Subject = subject,
#ProfileName = ProfileName
from BatchEmail
where processed = 0
if(#BatchEmailID is null)
break;
exec msdb.dbo.sp_send_dbmail #To, #Body, #Subject, #ProfileName
update BatchEmail
set processed = 1
where BatchEmailID = #BatchEmailID
end
If you still would rather not have a separate service and insist on doing all of this in the trigger, you could turn #BatchEmail into a table variable, still with a #Processed bit column and select 1 row at a time, updating #Processed after each row.

Automatic birthday email send out of MS SQL

I created a table named Customers with the following columns:
ID
Name
Birthdate
Email
Manager
Entries
Name Birthdate Email Manager
---- ---------- -------------- -------
Jan 1986-05-15 jan#gmail.com None
Koos 1986-07-24 Koos#gmail.com 1
Sam 1991-07-24 Sam#gmail.com 1
Meaning that Koos and Sam's manager is Jan.
Now what I want to do is to determine if someone has a birthday today.
Create PROCEDURE [dbo].[EmailData]
AS
SET NOCOUNT ON
BEGIN
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
select
a.ID, a.Name,
a.BirthDate as EmpBirthday,
b.Name as Manager,
b.Email as ManEmail
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
END
Go
The above works fine, I get the results I want 100%.
What I want to do now is send the email of each employee whose birthday it is today to the employee's manager.
I came up with this but it does not seem to work and I have no idea why:
Create PROCEDURE EmailData
AS
BEGIN
Declare #email nvarchar(128)
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
DECLARE rcpt_cursor CURSOR FOR
select
b.Email
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
OPEN rcpt_cursor
FETCH NEXT FROM rcpt_cursor INTO #email
DECLARE #ServerAddr nvarchar(128)
Set #ServerAddr = 'smtp.gmail.com'
DECLARE #From nvarchar(128)
Set #From = 'My Email Address'
DECLARE #Bodytext nvarchar(512)
Set #BodyText = 'Your Employees have a birthday today.'
DECLARE #User nvarchar(128)
Set #User = 'My Email Address'
DECLARE #Password nvarchar(128)
Set #Password = 'My Password'
DECLARE #SSL int
Set #SSL = 0
DECLARE #Port int
Set #Port = 587
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
DECLARE #subject nvarchar(255)
SELECT #Subject = 'Happy Birthday' + #email
EXEC EmailData #ServerAddr,
#from, #email, #subject, #BodyText, #User, #Password, #SSL, #Port
FETCH NEXT FROM rcpt_cursor
INTO #email
END
CLOSE rcpt_cursor
DEALLOCATE rcpt_cursor
End
Go
Any help will be appreciated.
Ok I cracked it
First of all I set up a Database mail account
Then I wrote a procedure
USE [Customers]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_SendEmail]
AS
Declare #email nvarchar(128)
Declare #name nvarchar(128)
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
DECLARE rcpt_cursor CURSOR FOR
select
a.Name,
b.Email
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
OPEN rcpt_cursor
FETCH NEXT FROM rcpt_cursor
INTO #name,#email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
Declare #subject nvarchar(255)
Declare #Bodytext nvarchar(512)
Set #BodyText = #Name + '' + ' has a birthday today. Please congrad him'
Set #Subject = 'Employee Birthday'
exec msdb.dbo.sp_send_dbmail
#Profile_Name = 'HappyBirthday',
#Recipients = #email,
#Body = #BodyText,
#Subject = #Subject
FETCH NEXT FROM rcpt_cursor
INTO #name,#email
END
CLOSE rcpt_cursor
DEALLOCATE rcpt_cursor
Go
Exec [usp_SendEmail]
The procedure now run through the hole customer table check if there are birthday dates that much and send the email to the managers whose employees have a birthday.

Resources