Is sp_send_dbmail with #query param synchronous - sql-server

Let's imagine that I have a table ExchangeSession with field Processed.
I need to send e-mail with all ExchangeSession with Processed = 0 and then update these sessions to Processed = 1. Like this:
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SomeProfile',
#recipients='SomeAddress',
#subject = 'SomeSubject',
#body = #bodyparam,
#body_format = 'TEXT',
#query = N'SET NOCOUNT ON; select * from ExchangeSession where Processed = 0',
#execute_query_database = #dbnameparam,
#attach_query_result_as_file = 1,
#query_attachment_filename = 'report.csv',
#query_result_header = 1,
#query_result_width = 32767,
#query_result_separator = #delimiterparam,
#exclude_query_output = 1,
#append_query_error = 0,
#query_no_truncate = 0,
#query_result_no_padding = 1;
update ExchangeSession
set
Processed = 1
where
Processed = 0
Will sp_send_dbmail executes my query synchronously relatively to the batch or asynchronously? In other words is there any chance that in the e-mail I'll not get all unprocessed sessions?

The answer is YES. query param execution is synchronous relatively to the batch.
Made the following test:
insert dbo.[CallLog]([Name]) values ('BeforeMail')
set #queryparam = N'SET NOCOUNT ON;
insert dbo.[CallLog]([Name]) values (''Attachment'');
SELECT
0 as [ID],
...'
...
EXEC msdb.dbo.sp_send_dbmail
...
#query = #queryparam,
...
insert dbo.[CallLog]([Name]) values ('AfterMail')
And voila, the results in CallLog:
LogID Name
801 BeforeMail
802 Attachment
803 AfterMail
Also I looked at sysmail_allitems and sysmail_mailattachments. The send_request_date value in the sysmail_allitems is exactly the same as last_mod_date value in the sysmail_mailattachments.
And the value of sent_date in the sysmail_allitems slightly differs.
So sp_send_dbmail executes the query synchronously in the batch, saves results into a file and then later on send an e-mail.

I am not 100% certain, but I would assume that it is possible that new entries could sneak into your ExchangeSession table between sending the email and updating the records. I would play safe by doing:
update ExchangeSession
set Processed = 2 -- This procedure should be the only place that sets Processed to 2
where Processed = 0
EXEC msdb.dbo.sp_send_dbmail
....
#query = N'SET NOCOUNT ON; select * from ExchangeSession where Processed = 2',
....
update ExchangeSession
set Processed = 1
where Processed = 2

Related

Sending mail only once in a stored procedure SQL Server executed by scheduled task

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".

msdb.dbo.sp_send_dbmail Not sending messages when using isnull

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)'

DB Trigger for Sending an email not working

I have a custom database sql Trigger which inserts a new row if the previous condition has been satisfied.
The problem is, the confirmed_Erp flag on the database does not get actioned if I include the email section of the code below which I have commented out.
Do I need to add a delay or how could I rewrite this to ensure that the confirmed_Erp flag is set to 1 and therefore the email will send. one of the email conditions is confirmed_erp = 1
There is an app which is running on top of the database which runs jobs and once a job completed the confirmed_erp flag in the database changes to 1.
Code:
begin
select #new_id = max(id) from jobr_generic;
insert jobr_generic (id, type, jobr_ts, customerno, str_attr1,str_attr3,
dec_attr1, str_attr4,str_attr5, str_attr6,
str_attr7,str_attr8,str_attr9,str_attr10,str_attr11,str_attr12,str_attr13,str_attr14,str_attr15,str_attr16,str_attr17,str_attr18,str_attr19,str_attr20, send_erp,species, maincut, subcut, meatgroup, packtype, factorycode, supplyingsite, currency)
values (#new_id+1, '235', getdate(), #customerno,#attr1, #attr3, #decattr1, #attr4, #attr5, #attr6, #attr7, #attr8, #attr9, #attr10, #attr11, #attr12, #attr13, #attr14, #attr15, #attr16, #attr17, #attr18, #attr19, #attr20, 1, #species,#maincut,#subcut,#meatgroup,#packtype,#factorycode,#supplyingsite,#currency);
end
if #type ='235' AND #CONFIRMED_ERP = 1 AND #itemrequestfor = 'P'
begin
declare #body2 varchar(1024)
set #body2 = 'Primal Item: '+#attr16+ ' has been created and is ready
for use.'
exec msdb.dbo.sp_send_dbmail
#profile_name = 'DoNotReplyMailProfile',
#recipients= 'test#test.com',
#copy_recipients= 'test1#test.com',
#subject = 'A Primal Item has been Created',
#body = #body2,
#body_format = 'TEXT',
#exclude_query_output = 1 ;
end

SQL Server stored procedure: wait for a delete's trigger to finish before continuing in the procedure

My stored procedure executes a delete statement that sets off a trigger that can't record who deleted the row, but records a blank for changed_by.
Then the stored procedure updates the changed_by with the username it was given.
Half the time, part 2 of the below stored procedure finds the results of the trigger, the other half of the time, it doesn't find the results of the trigger, so there is nothing to update.
How can I "yield" control and ensure the update's trigger finishes before continuing with the stored procedure?
(In comments you see some things I've tried so far that haven't worked)
DROP PROCEDURE IF EXISTS dbo.deleteAndUpdateChangedByInAuditTrail
GO
CREATE PROCEDURE dbo.deleteAndUpdateChangedByInAuditTrail
(#tableName VARCHAR(100),
#pkIDColName VARCHAR(100),
#pkIDValue NUMERIC,
#delUser VARCHAR(100) )
AS
BEGIN TRANSACTION;
-- PART 1: DO THE DELETE:
DECLARE #JUST_BEFORE_DELETION_TIMESTAMP AS DATETIME2;
SET #JUST_BEFORE_DELETION_TIMESTAMP = CONVERT(varchar, SYSDATETIME(), 121);
DECLARE #DELETION_TEMPLATE AS VARCHAR(MAX);
SET #DELETION_TEMPLATE = 'delete from {THE_TABLE_NAME} WHERE {PK_ID_COL_NAME} = {PK_ID_VALUE}';
SET #DELETION_TEMPLATE = REPLACE(#DELETION_TEMPLATE, '{THE_TABLE_NAME}', #tableName);
SET #DELETION_TEMPLATE = REPLACE(#DELETION_TEMPLATE, '{PK_ID_COL_NAME}', #pkIDColName);
SET #DELETION_TEMPLATE = REPLACE(#DELETION_TEMPLATE, '{PK_ID_VALUE}', #pkIDValue);
--PRINT #DELETION_TEMPLATE
EXEC (#DELETION_TEMPLATE);
COMMIT TRANSACTION;
BEGIN TRANSACTION;
-- PART 2: UPDATE THE AUDIT_TRAIL:
DECLARE #TOTAL_NUM_ROWS_UPDATED_WITH_USERNAME AS NUMERIC;
SET #TOTAL_NUM_ROWS_UPDATED_WITH_USERNAME = 0;
--DECLARE #TOTAL_TRIES_SO_FAR AS NUMERIC;
--SET #TOTAL_TRIES_SO_FAR = 0;
--WHILE #TOTAL_NUM_ROWS_UPDATED_WITH_USERNAME < 1 AND #TOTAL_TRIES_SO_FAR < 5
--BEGIN
--SET #TOTAL_TRIES_SO_FAR = #TOTAL_TRIES_SO_FAR + 1;
--WAITFOR DELAY '00:00:01.000' -- SEEN IT FAIL FOR 4 SECONDS :(
DECLARE #UPDATE_AUDIT_TRAIL_TEMPLATE AS VARCHAR(MAX);
SET #UPDATE_AUDIT_TRAIL_TEMPLATE = 'update AUDIT_TRAIL set changed_by = ''{CHANGED_BY}'' WHERE upper(table_name) = upper(''{THE_TABLE_NAME}'') and table_pk_value = {PK_ID_VALUE} and CONVERT(varchar, changed_at, 121) >= ''{CHANGED_AT}'' ';
SET #UPDATE_AUDIT_TRAIL_TEMPLATE = REPLACE(#UPDATE_AUDIT_TRAIL_TEMPLATE, '{CHANGED_BY}', #delUser);
SET #UPDATE_AUDIT_TRAIL_TEMPLATE = REPLACE(#UPDATE_AUDIT_TRAIL_TEMPLATE, '{THE_TABLE_NAME}', #tableName);
SET #UPDATE_AUDIT_TRAIL_TEMPLATE = REPLACE(#UPDATE_AUDIT_TRAIL_TEMPLATE, '{PK_ID_VALUE}', #pkIDValue);
SET #UPDATE_AUDIT_TRAIL_TEMPLATE = REPLACE(#UPDATE_AUDIT_TRAIL_TEMPLATE, '{CHANGED_AT}', #JUST_BEFORE_DELETION_TIMESTAMP);
--PRINT #UPDATE_AUDIT_TRAIL_TEMPLATE
EXEC (#UPDATE_AUDIT_TRAIL_TEMPLATE);
SELECT #TOTAL_NUM_ROWS_UPDATED_WITH_USERNAME = ##ROWCOUNT;
--END
COMMIT TRANSACTION;
RETURN #TOTAL_NUM_ROWS_UPDATED_WITH_USERNAME;
GO
Triggers don't get executed asynchronously. The next step after the DELETE will not happen until the trigger is finished.
If you are seeing something that makes you think otherwise, there is some other reason for it. It's not because the trigger "didn't finish".

SP_SEND_DBMAIL #QUERY Inserts Line Breaks Into E-mail

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),'')

Resources