DB Trigger for Sending an email not working - sql-server

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

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

Send Alert based on condition

We run SQL Server 2012. I'd like to generate an alert email if a condition is met. This would be setup in SQL Server Agent and run every hour.
Basically would run the following query:
SELECT
IBTRANSACTIONID, SUBCONSTATUS
FROM
PSAPMSGSUBCON
WHERE
SUBCONSTATUS = 3
AND STATUSSTRING = 'WRKNG'
IF --(need some input here)
EXEC msdb.dbo.sp_send_dbmail --(then would call this to issue email)
Appreciate replies
You may try this one.
DECLARE
#IBTRANSACTIONID VARCHAR(100) = NULL,
#SUBCONSTATUS VARCHAR(100) =NULL
SELECT
#IBTRANSACTIONID = IBTRANSACTIONID,
#SUBCONSTATUS = SUBCONSTATUS
FROM
PSAPMSGSUBCON
WHERE
SUBCONSTATUS = 3
AND STATUSSTRING = 'WRKNG'
IF (#IBTRANSACTIONID IS NOT NULL AND #IBTRANSACTIONID <>'') AND (#SUBCONSTATUS IS NOT NULL OR #IBTRANSACTIONID <>'')
BEGIN
EXEC msdb.dbo.sp_send_dbmail --(then would call this to issue email)
END

Is sp_send_dbmail with #query param synchronous

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

Automates sending email using SQL Server 2008 T-SQL, with custom message title and body

This question is the follow-up of this thread
Given that I've created two simple tables User and Item:
User (Id, Name, Email)
Item (Id, CreatedDate, EmailSent, UserId)
I am able to create an SQL Server Agent job that periodically runs the following script:
USE test
DECLARE #emails varchar(1000)
SET #emails = STUFF((SELECT ';' + u.Email FROM [user] u JOIN [item] i
ON u.ID = i.UserId
WHERE i.EmailSent = 0 AND DATEDIFF(day, i.CreatedDate, GETDATE()) >= 30
FOR XML PATH('')),1,1,'')
/** send emails **/
EXEC msdb.dbo.sp_send_dbmail
#profile_name='test',
#recipients=#emails,
#subject='Test message',
#body='This is the body of the test message.'
The purpose is to get any item that has been created for >= 30 days and then send a reminder email to its user. The EmailSent is checked first to exclude items already reminded.
I want to improve this script as follows:
There's a separate query that does the SELECT part and stores the result into a variable so that it can be reused in the email sending query, and in an UPDATE query that sets selected items' EmailSent to True (done after emails sent).
Customise the title and body of the message with user's Name and item's Id. Something like: Dear Name, the item Id has been created for 30 days or more.
Anybody has any idea of how these improvement could be done?
I've managed to have this so far
USE test
-- 1) Update selected items to state 1
UPDATE [PickingList]
SET ReminderState = 1
WHERE ReminderState = 0 AND DATEDIFF(day, CreatedDate, GETDATE()) >= 30
DECLARE #userId INT
DECLARE #email NVARCHAR(100)
DECLARE #plId INT
DECLARE #getId CURSOR
-- 2) Process those having state 1
SET #getId = CURSOR FOR
SELECT u.ID, u.Email, pl.ID
FROM [User] u JOIN [PickingList] pl
ON u.ID = pl.UserId
WHERE pl.ReminderState = 1
OPEN #getId
FETCH NEXT
FROM #getId INTO #userId, #email, #plId
WHILE ##FETCH_STATUS = 0
BEGIN
/** send emails **/
DECLARE #pId VARCHAR(1000)
DECLARE #title VARCHAR(1000)
DECLARE #body VARCHAR(8000)
SET #pId = CAST(#plId AS VARCHAR(16))
SET #title = #pId + ' was created more than 30 days ago'
SET #body = 'The following picking list ' + #pId + ' blah blah'
DECLARE #code INT
SET #code = -1
EXEC #code = msdb.dbo.sp_send_dbmail
#profile_name='test',
#recipients=#email,
#subject=#title,
#body=#body
-- 3) Log the email sent and update state to 2
-- Below is what I want to do, but ONLY when the it can be sure that
-- the email has been delivered
INSERT INTO [PickingListEmail] (UserId, PickingListId, SentOn)
VALUES (#userId, #plId, GETDATE())
UPDATE [PickingList] SET ReminderState = 2 WHERE ReminderState = 1
FETCH NEXT
FROM #getId INTO #userId, #email, #plId
END
CLOSE #getId
DEALLOCATE #getId
In step (3), before saving the email sent and update the item to state 2 (processed), I would want to make sure that the email has been sent, based on the data fetched from sysmail_log, as sp_send_dbmail would only care about whether it can send the mail to the SMTP server, so will return the success code 0 even when the sending fails.
Something like this:
meaning of the values of sent_status on msdb.dbo.sysmail_mailitems
or
Check If sp_send_dbmail Was Successful

Do stored procedures lock tables/rows?

Quite a simple question. In SQL 2008 if I have a stored procedure (see below) do I run the risk of a race condition between the first two statements or does the stored procedure put a lock on the things it touches like transactions do?
ALTER PROCEDURE [dbo].[usp_SetAssignedTo]
-- Add the parameters for the stored procedure here
#Server varchar(50),
#User varchar(50),
#UserPool varchar(50)
AS
BEGIN
SET NOCOUNT ON;
Declare #ServerUser varchar(50)
-- Find a Free record
SELECT top 1 #ServerUser = UserName
from ServerLoginUsers
where AssignedTo is null and [TsServer] = #Server
--Set the free record to the user
Update ServerLoginUsers
set AssignedTo = #User, AssignedToDate = getdate(), SourcePool = #UserPool
where [TsServer] = #Server and UserName = #ServerUser
--report record back if it was updated. Null if it was not available.
select *
from ServerLoginUsers
where [TsServer] = #Server
and UserName = #ServerUser
and AssignedTo = #User
END
You could get a race condition.
It can be done in one statement:
You can assign in an UPDATE
The lock hints allow another process to skip this row
The OUTPUT clause returns data to the caller
Try this... (edit: holdlock removed)
Update TOP (1) ServerLoginUsers WITH (ROWLOCK, READPAST)
OUTPUT INSERTED.*
SET
AssignedTo = #User, AssignedToDate = getdate(), SourcePool = #UserPool
WHERE
AssignedTo is null and [TsServer] = #Server -- not needed -> and UserName = #ServerUser
If not, you may need a separate select
Update TOP (1) ServerLoginUsers WITH (ROWLOCK, READPAST)
SET
-- yes, assign in an update
#ServerUser = UserName,
-- write
AssignedTo = #User, AssignedToDate = getdate(), SourcePool = #UserPool
OUTPUT INSERTED.*
WHERE
AssignedTo is null and [TsServer] = #Server -- not needed -> and UserName = #ServerUser
SELECT ...
See this please for more: SQL Server Process Queue Race Condition

Resources