I have a code to send Birthday Emails to Customers, the query works fine, but the SQL Mail server always send a Birthday Email to all Customers, even he has no Birthday
use Insurance
go
select
Customer.CustomerID
,Customer.FirstName
,Customer.LastName
,Customer.Birthday
,Customer.Email
from Customer
where Customer.CustomerID = Customer.CustomerID and
DAY([Birthday]) = DAY(GETDATE())
AND MONTH([Birthday]) = MONTH(GETDATE())
declare #Receipientlist nvarchar(4000)
set #Receipientlist =
STUFF((select ';' + Email FROM dbo.Customer FOR XML PATH('')),1,1,'')
EXEC msdb.dbo.sp_send_dbmail #profile_name='test',
#recipients=#Receipientlist,
#subject='Insurance',
#body='Happy Birthday.
Today is your Birthday.'
Your query at the top of your batch has nothing to do with your statement that executes msdb.dbo.sp_send_dbmail. If you only want to email customer's who's birthday it is, you need to filter in the statement that creates the recipients (and you don't need the previous statement):
DECLARE #RecipientList nvarchar(4000);
--I removed the CustomerID = CustomerID clause, as it'll always equal itself,
--apart from when it's value is NULL (and I doubt it'll ever be NULL)
SET #RecipientList = STUFF((SELECT N';' + Email
FROM dbo.Customer
WHERE DAY([Birthday]) = DAY(GETDATE())
AND MONTH([Birthday]) = MONTH(GETDATE())
FOR XML PATH(N''),TYPE).value('.','nvarchar(4000)'), 1, 1,N'');
EXEC msdb.dbo.sp_send_dbmail #profile_name = 'test',
#recipients = #RecipientList,
#subject = 'Insurance',
#body = 'Happy Birthday.
Today is your Birthday.';
I've also changed the way that the value from the subquery is returned, used TYPE and the value clauses. Email addresses can contain some special characters, and these would have been escaped without the usage of TYPE (for example & would become &). (I've also corrected the spelling of #RecipientList.)
Related
I have a SQL Server stored procedure, which runs via a scheduled task every 7 minutes, and which is used to send an email to the recipients concerned, once the condition of the number of records returned by request is greater than or equal to 1.
the procedure code is:
Begin
declare #recordCount int
declare #bodytext varchar(200)
select #recordCount = isnull(count(*), 0)
from table where .............
IF (#recordCount > 0)
set #bodytext = N'Message'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Profile',
#recipients = 'Test#test.com',
#subject = 'Title',
#Body = #bodytext,
#query = N'Select Column1,Column2
from Table
inner join Table2 on .........
where ........................',
#execute_query_database = N'DB'
End
End;
My problem is that I'd like to send the email, only once, since the scheduled task runs every 7 minutes, but i can't find the track how i can achieve that without adding new columns in the "Table".
I have four tables:
Activity:
ActivityID(PK) ActivityName CustomerID(FK) UserId(FK)
1 Lead Gen 1st 50 U1
2 Lead Gen 2nd 60 U2
Customer:
CustomerID(PK) CustomerNumber CustomerName
50 C0150 cust50 ltd
60 C0160 cust60 ltd
User:
UserID(PK) UserName Email
U1 Mr. X X#cat.com
U2 Mr. Y Y#cat.com
UserActivity:
UserActivityID(PK) UserID(FK) ActivityID(FK)
888 U1 1
889 U2 2
I want to send an email (i.e. Email:X#cat.com) to the users related to the activity (i.e. ActivityId:1) if any insert happens in Activity Table (SQL Server 2008-R2).
The email body should contain the ActivityId, ActivityName, CustomerNumber and CustomerName.
The trigger has to do the above mentioned and the result should be like this in the email:
ActivityID:1, ActivityName:Lead Gen 1st created for CustomerNumber: C0150 & CustomerName: cust50 ltd
Here is my code:
CREATE TRIGGER [dbo].[Activity_Insert_Mail_Notification]
ON [dbo].[Activity]
AFTER INSERT
AS
BEGIN
DECLARE #ActivityID varchar(2000)
DECLARE #ActivityName varchar (2000)
Select #ActivityID=inserted.ActivityID,#ActivityName=inserted.ActivityName
From inserted
DECLARE #CustomerNo varchar(2000)
DECLARE #CustomerName varchar(2000)
Select #CustomerNo = B.[CustomerNumber]
,#CustomerName= B.[CustomerName]
from [dbo].[Activity] A
inner join [dbo].[Customer] B
on A.[CustomerID]=B.[CustomerID]
DECLARE #email VARCHAR(2000)
SELECT #email = RTRIM(U.[Email]) + ';'
FROM [dbo].[Activity] A
left join [dbo].[UserActivity] UA
inner join [dbo].[User] U
on UA.[UserID]=U.[UserID]
on A.[ActivityID]=UA.[ActivityID]
WHERE U.[Email]<> ''
DECLARE #content varchar (2000)
= 'ActivityID:' + #ActivityId + ' '
+ ',ActivityName:' + #ActivityName + ' '
+ 'has been created for' + 'CustomerNumber: ' + #CustomerNo
+ ' ' + '&CustomerName: ' + #CustomerName
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'LEADNOTIFY'
,#recipients = #email
,#subject = 'New Lead Found'
,#body = #content
,#importance ='HIGH'
END
The problem is in my code that I can't fetch the customer data and email from the respective tables properly.
I have written some code below which will loop through all the effected rows and send an email for each.
However before you read that, I would highly recommend (as #HABO commented) on using a different approach. Triggers are fine for some tasks, but 2 key things you want to keep in mind when using triggers:
Ensure its obvious to anyone developing the system that there are triggers - there is nothing worse as a developer than finding stuff magically happening on what seems like a simple CRUD operation.
Do whatever you do in your trigger as fast as possible because you are holding locks which not only affect the current session, but could easily affect other users as well. Ideally therefore you want to be performing set-based operations, not RBAR (Row By Agonising Row) operations.
Sending emails is a terrible thing to do inside a trigger because its not uncommon to be forced to wait for an SMTP server to respond. If you wish to trigger emails, a better way is to use the trigger to insert the email data into a queuing table and then have a service elsewhere which de-queues these emails and sends them.
All that aside the following code shows one way to handle the Inserted pseudo-table when you want to perform RBAR operations. Because in SQL Server the Inserted pseudo-table (and the Deleted pseudo-table) will contain the number of rows effected by the operation i.e. 0-N. Also I've hopefully joined your tables to correctly obtain the required information.
CREATE TRIGGER [dbo].[Activity_Insert_Mail_Notification]
ON [dbo].[Activity]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ActivityID VARCHAR(2000), #ActivityName VARCHAR(2000), #CustomerNo VARCHAR(2000), #CustomerName VARCHAR(2000), #Email VARCHAR(2000), #Content VARCHAR(2000);
-- Get all the relevant information into a temp table
SELECT ActivityID, ActivityName, C.CustomerNumber, C.CustomerName, RTRIM(U.[Email]) + ';' Email, CONVERT(BIT, 0) Handled
INTO #ActivityTriggerTemp
FROM Inserted I
INNER JOIN Customer C on C.CustomerID = I.CustomerID
INNER JOIN UserActivity UA on UA.ActivityID = I.ActivityID
INNER JOIN [USER] U on U.UserID = UA.UserID;
-- Loop through the temp table sending an email for each row, then setting the row as 'handled' to avoid sending it again.
WHILE EXISTS (SELECT 1 FROM #ActivityTriggerTemp WHERE Handled = 0) BEGIN
SELECT TOP 1 #ActivityID = ActivityID, #ActivityName = ActivityName, #CustomerNumber = CustomerNumber, #CustomerName = CustomerName, #Email = Email
FROM #ActivityTriggerTemp
WHERE Handled = 0;
-- Build the body of the email
set #Content = 'ActivityID:' + #ActivityId + ' '
+ ',ActivityName:' + #ActivityName + ' '
+ 'has been created for' + 'CustomerNumber: ' + #CustomerNo
+ ' ' + '&CustomerName: ' + #CustomerName;
-- Send the email
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'LEADNOTIFY'
, #recipients = #Email
, #subject = 'New Lead Found'
, #body = #Content
, #importance ='HIGH';
UPDATE #ActivityTriggerTemp SET
Handled = 1
WHERE ActivityID = #ActivityID AND ActivityName = #ActivityName AND CustomerNumber = #CustomerNumber AND CustomerName = #CustomerName AND Email = #Email;
END;
END
I am using msdb.dbo.sp_send_dbmail to send email based upon query results. It works fine until I used and isnull in my query. The query returns the right results but a message is not triggered. When the field in question is not null it works fine.
I want to validate builddate against checkdate. When they do not match I want to send an email. I used isnull for cases when the table did not populate and builddate is null.
My base query is as follows:
SELECT
ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE())) AS
[builddate],
CONVERT(VARCHAR(10),GETDATE(),121)AS [checkdate]
FROM dbo. XXXX_BPCCustomer_Test
HAVING ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE()))
<>CONVERT(VARCHAR(10),GETDATE(),121)
the results are builddate=2010-01-03 and checkdate = 2020-01-3
the code that pushes the email is as follows:
DECLARE #recordcount INT
SELECT #recordcount = ISNULL(COUNT(*),0)
FROM [YCH-REPORTING\YCHANALYTICS].[x3v7].[dbo].[Dataset_BPCCustomer_Test]
HAVING ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE()))
<>CONVERT(VARCHAR(10),GETDATE(),121)
IF (#recordcount > 0)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SQL Mail',
#recipients = 'john.XXXXXXXXX.com;XXXXXXXXXXXXXXX',
#query = 'SELECT
ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE())) AS
[builddate],
CONVERT(VARCHAR(10),GETDATE(),121)AS
[checkdate],"Dataset_BPCCustomer_Test" as [Table]
FROM [XXXXXX\YCHANALYTICS].[x3XX].[dbo].
[Dataset_BPCCustomer_Test]
HAVING
ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE()))
<>CONVERT(VARCHAR(10),GETDATE(),121)'
,
#subject = ' DataSet did not update or isnull',
--#attach_query_result_as_file = 1,
--#query_result_header = 0,
#body =0;
END
It appears that it is not processing the isnull statement? Any suggestions and thanks in advance.
It looks like there are double-quotes ("Dataset_BPCCustomer_Test") inside #query, making the statement incorrect.
Those should each be two single quotes (apostrophes):
#query = 'SELECT
ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE())) AS
[builddate],
CONVERT(VARCHAR(10),GETDATE(),121)AS
[checkdate],''Dataset_BPCCustomer_Test'' as [Table]
FROM [XXXXXX\YCHANALYTICS].[x3XX].[dbo].
[Dataset_BPCCustomer_Test]
HAVING
ISNULL(MAX(convert(date,Builddate)),DATEADD(YEAR,-10,GETDATE()))
<>CONVERT(VARCHAR(10),GETDATE(),121)'
I use the #query perimeter in sp_send_dbmail to send out an e-mail with a list of warnings (the warnings are returned by the #query perimeter). The #query perimeter is listed as text in the e-mail. All of the records returned from the #query perimeter and displayed in the e-mail has a line break between them. ie. If I have 4 records, I would have 8 lines because of line breaks.
How do I turn the line breaks off? I read the the msdn article for sp_send_dbmail, but it didn't mention any attributes that could be changed that would affect the line breaks.
Code:
BEGIN
EXEC MSDB.DBO.sp_send_dbmail
#PROFILE_NAME = 'Alerts',
#RECIPIENTS = #MAIL,
#SUBJECT = #NEWSUBJECT,
#BODY = #NEWBODY,
#QUERY =
'SET NOCOUNT ON
DECLARE #HEXSTRING AS VARCHAR(100)
SET #HEXSTRING = (SELECT HEXADECIMAL_STRING FROM mydb.dbo.statusupdates
WHERE MACHINE_ID = ''1111'' AND DATEDIFF(MI, TIME_DATE_RECEIVED, GETDATE()) <= 60)
SELECT [Warning_Description] FROM mydb.DBO.BINARYTOTABLE(mydb.DBO.HEXTOBINARY(#HEXSTRING)) AS ABB1
JOIN mydb.DBO.WarningMessages ON mydb.DBO.WarningMessages.[Bit_Offset] = ABB1.BITPLACE
WHERE BITVALUE = 1 AND ALERT_LEVEL = ''WARNING''',
#QUERY_RESULT_HEADER = 0,
#ATTACH_QUERY_RESULT_AS_FILE = 0;
END
Old post, but probably worth checking #query_result_width parameter of sp_send_dbmail
See http://technet.microsoft.com/en-us/library/ms190307(v=sql.110).aspx
If the data width in a column exceeds #query_result_width (default of 256) a new line will be added.
Steve
I have always used CHAR(13)+CHAR(10) to create line breaks in tsql. You will have to replace it by ' '.
I ended up putting everything into the #BODY parameter of SP_SEND_DBMAIL. Query results from a SELECT statement could be concatenated with regular text into a string and you can use HTML to format the resulting text.
try this and see what you get ...
#query = REPLACE(#query,CHR(13),'')
i have a job scheduler that is suppose to send a daily collection report to 20 different id's based on 20 different divisions. Each division will receive 1 report of that particular division only. This is the query i have come up with .
DECLARE #xml NVARCHAR(MAX)
DECLARE #body NVARCHAR(MAX)
SET #xml = CAST((select tm.name as 'td','',h.name as 'td','',h.account_number AS 'td','',
SUM(bc.total_amount) AS 'td'
FROM MJP.dbo.tbl_bank_collection bc,
MJP.dbo.tbl_div_type_master tm,
MJP.dbo.tbl_div_header h
where bc.type_id = tm.id
and bc.header_id = h.id
and bc.transaction_date = '06-12-2012'
and bc.div_id in ( select d.id
from tbl_div d, tbl_bank_collection bc
where bc.div_id = d.id
group by d.id)
group by tm.name, h.name,h.account_number with rollup
having grouping(h.name) = 1 or
GROUPING(h.account_number) = 0
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET #body ='<html><i>Collection Report</i>
<body bgcolor=red><table border = 1><tr><th>Type Name
<th>Header Name</th><th>Account Number</th>
<th>Total Amount</th></tr>'
SET #body = #body + #xml +'</table></body></html>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name='alkesh mail',
#body_format ='HTML',
#recipients='id.no1#yahoo.com;id.no2#yahoo.com',
#subject='Daily Report',
#body=#body
----------------------------------------------------------------------------------------
Now i want to split the report after a particular divison's final sum amount is calculated and the next report should be generated for the next division' id.
Any suggestions or clarifications !!
I am assuming business politics has you constrained within SQL Server, because concatenating XML email reports is not what SQL Server is designed for.
Personally I would have a service executable (Windows Service, or just a Console Application called by task scheduler, whatever.) with code (e.g. C#) that:
Gets just the data form SQL Server, passing a divisionId paramter to get the report for just that division.
Open an XML template file and insert the data.
Request a list of users for that division from SQL Server, and email each person in the list.
Repeat for each division.
Having said all that you could do this with just stored procedures, using loops. You could use a cursor (yuck) but I always prefer these constructs: (NB. Looping in RDBMS's is something to be avoided really, it very much depends on your particular circumstances)
declare #divisionId int = (select min(divisionId) from tbDivisions)
declare #userId int = ( min(userId) from users where divisionId = #divisionId )
declare #xml nvarchar(max)
while divisionId is not null
begin
exec your_report_stored_procedure #division = #divisionId, output #xml = #xmlOut
while #userId is not null
begin
exec your_email_tored_procedure #userId = #userId, #xmlIn = #xml
select #userId = min(userId) from users where divisionId = #divisionId and userId > #userId
end
select #divisionId = min(divisionId) from tbDivisions where divisionId > #divisionId
end