I have used cursor to loop through my values and Every time i am sending a mail but if no result found then it was sending an empty mail,How can i avoid that?
This is very much guessed logic, but based on the very vague comments and question, I imagine you simply need to use an IF. This is Pseudo-SQL, however...
DECLARE #TableHTML varchar(MAX);
SELECT #TableHTML = {Your HTML data expression};
IF #TableHTML IS NOT NULL BEGIN
EXEC sp_send_dbmail #Subject = {Your Subject},
#Body = #TableHTML,
#....;
END
Related
Can someone please give an example of when we need to use a cursor in SQL that cannot be solved with a set based approaches.
Thanks
A cursor is often used when you need to do an action on a per row basis. Which is something we'd often relegate to other tools outside of the DBMS. In general the strength of the DBMS lies in set based approaches for data. However.. to give an example.
Say you have a table where some other process writes mail messages to be stored and sent at a later date. Perhaps multiple SQL jobs running and each writes their own status, then when next morning comes or the server load is very low, the DBMS is expected to send these on its own.
Setting up an example table with some data:
CREATE TABLE outgoingMessages
(
recipient VARCHAR(MAX),
subject NVARCHAR(255),
message NVARCHAR(MAX)
)
INSERT INTO dbo.outgoingMessages (recipient,subject,message)
VALUES
('foo#bar.com', N'An email', N'Procedure dbo.Foo ran with statuscode X'),
('foo#bar.com', N'An email', N'Procedure dbo.Bar ran with statuscode Y'),
('manager#bar.com', N'An email', N'Data synchronisation had problems, ask foo')
Then as a theoretical end step/end job, we have a process that goes over the table and handles all built up messages.
/* Scheduled job */
DECLARE mailCursor CURSOR FOR
SELECT * FROM dbo.outgoingMessages;
DECLARE #mailRecipient VARCHAR(MAX);
DECLARE #mailSubject NVARCHAR(255);
DECLARE #mailMessage NVARCHAR(MAX);
OPEN mailCursor;
FETCH NEXT FROM mailCursor INTO
#mailRecipient, #mailSubject, #mailMessage
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #mailRecipient + ' ' + #mailSubject + ' ' + #mailMessage
EXEC msdb.dbo.sp_send_dbmail
#profile_name = N'defaultMailprofile',
#recipients = #mailRecipient,
#subject = #mailSubject,
#body = #mailMessage
FETCH NEXT FROM mailCursor INTO
#mailRecipient, #mailSubject, #mailMessage
END
CLOSE mailCursor;
DEALLOCATE mailCursor;
This makes the database print out all lines individually and send a mail to the specified variables (calls another stored procedure for it) for each line in the table. I would say this line of operation, taking data for each row and manipulating it further or using it as variables for another procedure is a more common usecase.
/* Print results */
foo#bar.com An email Procedure dbo.Foo ran with statuscode X
foo#bar.com An email Procedure dbo.Bar ran with statuscode Y
manager#bar.com An email Data synchronisation had problems, ask foo
You can think of a table with perhaps built up API calls by other automated processes, then to be executed at a later date.
Are cursors common? No. You should always consider their usecases and ideally use a different approach. But if you need to do something for each line, and potentially jump backwards based on conditions. Cursors allow you to do so inside the DBMS and they're a powerful tool.
Like the title says, I'm trying to make a stored procedure used to send emails to addresses stored in the database, often including multiple recipients. I've tried a few approaches to this, but I think I'm hitting a wall and would appreciate any input anyone has. The main issue I've had is querying the database for the addresses and passing that to sp_send_dbmail in a way that works.
The address table is pretty simple, looks something like this, but much larger. Both columns are varchars:
idNumber | email
__________________________
a123 | steve#test.com
a123 | carol#test.com
1-100 | mary#test.com
So if we're sending to ID number "a123", an email needs to go to both Steve and Carol.
Here's a super simple procedure. It's not verbatim since this is all on my work computer, but more of a skeletal gist of what I'm going after.
CREATE PROCEDURE sendMail
#idNumber varchar(MAX),
#subject varchar(MAX),
#body varchar(MAX)
EXEC msdb.dbo.sp_send_dbmail
#recipients = "EXEC SELECT email FROM emailTable WHERE idNumber = " + #idNumber + "';",
#subject = #subject,
#body = #body;
It throws and error; it doesn't seem to like concatenating the ID parameter into the query. I tried making a separate procedure to query emails, but passing the ID parameter to the procedure didn't seem to work either. Even if I did successfully pass the parameter and get the query to execute successfully, I'd still need to join the two results in a single string and delimit them with semicolons so they'll play nice with sp_send_dbmail. I think?
SQL wizards, how would you approach this? I'm not wildly experienced, so is there something simple and syntactic I'm doing wrong? Is my approach flawed fundamentally?
I appreciate your input. Thank you.
EDIT: Here's Kritner's working solution! Thanks a bunch!
CREATE PROCEDURE testMail2
#idNumber varchar(MAX)
AS
BEGIN
DECLARE #recipientList varchar(MAX)
SET #recipientList = (STUFF((SELECT ';' + email FROM emailTable WHERE idNumber = #idNumber FOR XML PATH(' ')),1,1,''))
EXEC msdb..sp_send_dbmail
#recipients=#recipientList,
#subject='Subject Line',
#body='Body Text'
END
You can do a query and assign the values to a variable as such:
DECLARE #myRecipientList varchar(max)
SET #myRecipientList = (STUFF((SELECT ';' + emailaddress FROM table FOR XML PATH('')),1,1,''))
This will set your #myRecipientLIst to a ";" delimited list of the recipients specified your query.
You could also do the same sort of idea with a SP, just throw them into a temp/variable table and stuff into a semi colon separated list.
EDIT:
Finally to send the mail you could do:
EXEC msdb.dbo.sp_send_dbmail
#recipients = #recipientList,
#subject = #subject,
#body = #body // ........
COMMENT EDIT:
based on your original query, your stuff query should look something like this:
DECLARE #myRecipientList varchar(max)
SET #myRecipientList = STUFF((SELECT ';' + email FROM emailTable WHERE idNumber = #idNumber FOR XML PATH('')),1,1,'')
The idea behind this is - for every email found in the email table append to #myrecipientList the email found and a semi colon.
I used to have triggers in my database that used cursors / sp_send_dbmail to email when certain columns were updated. I was told this was not best practice so I created a new table called EmailNotify that contains columns like recepient, subject, body etc. So instead the triggers now insert into this table the email I want to send.
I want to create a Job that runs every few minutes that checks this table and emails. The item below is what I came up with but is it okay to use cursors in this case? Should the table include a sent field so I know which rows I sent? Can I change that inside the cursor? Or would it be recommended to truncate the table afterwards?
DECLARE #emailSubject AS NVARCHAR(100);
DECLARE #emailRecipients AS NVARCHAR(100);
DECLARE #emailBody AS NVARCHAR(max);
DECLARE cur CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
recipients,
subject,
body
FROM
EmailNotify;
OPEN cur
FETCH NEXT FROM cur INTO
#emailRecipients,
#emailSubject,
#emailBody
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'name',
#recipients = #emailRecipients,
#body = #emailBody,
#body_format = 'HTML',
#subject = #emailSubject;
FETCH NEXT FROM cur INTO
#emailRecipients,
#emailSubject,
#emailBody
END
CLOSE cur
DEALLOCATE cur
I would
Add an EmailSent(DATETIME) = NULL column to your table
Create an EmailSent variable at the top of your proc and set it to GETDATE()
UPDATE <YOURTABLE> SET EmailSent = #EmailSent WHERE EmailSent IS NULL
Add a WHERE EmailSent = #EmailSent to your query for the email
You can accomplish this with a SSRS report with a subscription or data-bound subscription instead of this job, but it's really just a matter of preference. Formatting is easier in SSRS, otherwise you're messing with dynamic HTML in your message body. Been there, not pleasant.
We have a stored procedure that is supposed to check db and select all records where sentFlag is No.
Once record(s) found, the stored proc invokes sp_send_dbmail with passed parameters and then sends an email to affected individuals.
This appears to work.
The issue we are having so far though is that each indivual is receiving duplicate emails.
Any ideas what part of this code could be causing this?
OPEN MAIL_CURSOR
FETCH MAIL_CURSOR into #mail1, #sender,#content1
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #email = #email+';'+Email
FROM GRVRIEVANCES
WHERE sentFlag = 'No'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Grievances',
#recipients = #email,
#subject = 'Account Details',
#body = #content1;
FETCH MAIL_CURSOR INTO #mail1, #sender, #content1
END
CLOSE MAIL_CURSOR
DEALLOCATE MAIL_CURSOR
END
If you set email to an initial value within the loop, does the issue go away? Also, make sure you setting the sentflag to 'yes'.
WHILE ##FETCH_STATUS = 0
BEGIN
SET #email=''
SELECT #email = #email+';'+Email
Back to basics to solve this one.
Start with some debugging:
Comment out your EXEC part
Add PRINT #email in the same spot
Run the cursor and see the reults, they should be quite enlightening!
Essentially what you're doing is on every cursor execution you are building up this big ol' string of email addresses for all GRVRIEVANCES WHERE sentFlag = 'No'.
I'm trying to send an email using sp_send_dbmail in Sql Server 2005. I have both body text and a query being sent as an attachment.
Sometimes, however, the query will return an empty dataset.
Is there any way for me to test the results of the dataset before I send out the email, and if it has no results, not send it as an attachment.
I was thinking to maybe run the query before I send the email, and to test the results that way. Then, I'd have an if-else as follows:
if ##rowcount >0
EXEC msdb.dbo.sp_send_dbmail #recipients=#recipients_list,
#subject = #subject,
#body = #body_text, #body_format = 'HTML',
#query = #query,
#attach_query_result_as_file = 1,
#query_result_width = 4000,
#query_attachment_filename = 'Details.txt'
else
EXEC msdb.dbo.sp_send_dbmail #recipients=#recipients_list,
#subject = #subject,
#body = #body_text, #body_format = 'HTML'
But I don't think this is an efficient way to solve the problem.
Any suggestions? TIA!!
Alright, I couldn't test this on my set up at work since it appears our DBAs have blocked access to this procedure. However, I know that sp_send_dbmail will not use local variables in the calling script, but it might let you use a global temp table.
This isn't a great solution, but you could try inserting your query result set into ##tempTable, then changing the query you pass to sp_send_dbmail to select * from ##tempTable if there are > 0 rows.
That should at least be better than running the original query 2x (if it works). Remember to drop it when you're done!