SQL Email Trigger - inserted row - sql

I am trying to create a trigger which should shoot a mail as soon a record is added to a said table. For this purpose I have created a the following trigger, which is sort of catering my requirement:
ALTER TRIGGER [dbo].[EMAIL_ALERT_NEW]
ON [dbo].[APTCR_LOG]
after insert
as
BEGIN
SET NOCOUNT ON;
DECLARE #body NVARCHAR(MAX) = N'';
SELECT AUDTUSER, NAMERMIT ,AMTRMITTC,IDRMIT FROM APTCR_LOG inserted
SELECT #body += CHAR(13) +++ CHAR(10) +++ RTRIM(AUDTUSER) +++ RTRIM(NAMERMIT) +++RTRIM(AMTRMITTC) +RTRIM(IDRMIT) FROM APTCR_LOG inserted;
--SELECT NAMERMIT, AUDTUSER ,AMTRMITTC FROM APTCR_LOG WHERE AMTRMITTC > 100000
BEGIN
--SELECT AUDTUSER, NAMERMIT ,AMTRMITTC,IDRMIT FROM APTCR_LOG inserted WHERE AMTRMITTC > 100000.000
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'ublaze#gmail.com',
#profile_name = 'default',
#subject = 'NEW PAYMENT',
#body = #body;
END
end
The issue is it's sending all the records on the table where I just need the last record which was added in to the table.
Can somebody please help with this situation?

Related

T SQL After Update Trigger on column get the value changed

I've only started exploring with triggers recently. I've set up my database mail function and all is working well. My trigger works perfectly if the column value is changed, but how do I get the value/row that changed?
USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[triggerName] ON [dbo].[tableName]
FOR UPDATE
AS
declare #cust varchar(100);
if update(Column)
BEGIN
SET NOCOUNT ON
set #cust = THE VALUE MUST GO HERE I ASSUME
END
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'someone#example.com',
#profile_name = 'ProfileName',
#subject = 'Customer Information Changed',
#body = #cust;
Compare the values in inserted and deleted. If they are not the same, then the value changed.

SQL Server Trigger to send email on insert with conditions and using table values

I have made a trigger to run upon insert of record, which should first check the current time and if it is beyond a particular hour and minute, it should fire the trigger and should take data from a table and use those values in the subject and body of the email.
Below is the trigger which is working fine but i need to add time condition and I tried to query a table based on the record inserted into the table but I am not getting the desired email.
As you can see that I tried to use a table value to be sent in the subject but I get the email with SQL Server Message in the subject.
Another thing I want to add is to query the table and check if the INSERTED.USERID is available in the table or not, if it is not available, then no need to fire the trigger.
USE [Attendance]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[after_insert_ATT_LOG] ON [dbo].[ATT_LOG]
FOR INSERT
AS
BEGIN TRY
DECLARE #PositionCode NVARCHAR(5), #FullNameE NVARCHAR(45), #FullNameE2 NVARCHAR(40), #Email NVARCHAR(50), #EDescription NVARCHAR(100), #ReportingToShortNo NVARCHAR(5), #ReportingtoFullNameE NVARCHAR(45), #ReportingEmail NVARCHAR(50)
Select #PositionCode=ED.POsitionCode, #FullNameE=EP.FullNameE,#FullNameE2=ED.FullNameE, #Email=ED.Email, #EDescription=EP.EDescription, #ReportingToShortNo=EP.ReportingToShortNo, #ReportingtoFullNameE=EP.ReportingtoFullNameE, #ReportingEmail=(Select Email
FROM HRSystem.dbo.employeedetails WHERE PositionCode= EP.ReportingToShortNo) FROM HRSystem.dbo.employeedetails ED, HRSystem.dbo.Position EP WHERE ED.PositionID = EP.PositionID AND ED.PositionCode = (Select INSERTED.USERID FROM INSERTED) COLLATE DATABASE_DEFAULT;
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'abc#abc.com',
#profile_name = 'Alert',
#subject = #FullNameE,
#body = 'Test Alert';
END TRY
BEGIN CATCH
DECLARE #dummy int
SET #dummy = 1
END CATCH
as for your second concern
Another thing I want to add is to query the table and check if the INSERTED.USERID is available in the table or not, if it is not available, then no need to fire the trigger.
a FOR INSERT trigger fires right after a row is inserted. meaning, the USERID you wanna look for is sure to be in table ATT_LOG.
and as for your first issue. please try the t-sql code below:
CREATE TRIGGER [dbo].[after_insert_ATT_LOG] ON [dbo].[ATT_LOG]
FOR INSERT
AS
BEGIN TRY
DECLARE #PositionCode NVARCHAR(5), #FullNameE NVARCHAR(45), #FullNameE2 NVARCHAR(40), #Email NVARCHAR(50), #EDescription NVARCHAR(100), #ReportingToShortNo NVARCHAR(5), #ReportingtoFullNameE NVARCHAR(45), #ReportingEmail NVARCHAR(50)
Select #PositionCode=ED.POsitionCode, #FullNameE=EP.FullNameE,#FullNameE2=ED.FullNameE
, #Email=ED.Email, #EDescription=EP.EDescription
, #ReportingToShortNo=EP.ReportingToShortNo
, #ReportingtoFullNameE=EP.ReportingtoFullNameE
, #ReportingEmail=
(Select Email
FROM HRSystem.dbo.employeedetails
WHERE PositionCode= EP.ReportingToShortNo)
FROM HRSystem.dbo.employeedetails ED, HRSystem.dbo.Position EP
WHERE ED.PositionID = EP.PositionID
AND ED.PositionCode =
(Select INSERTED.PositionCode FROM INSERTED) COLLATE DATABASE_DEFAULT;
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'abc#abc.com',
#profile_name = 'Alert',
#subject = #FullNameE,
#body = 'Test Alert';
END TRY
BEGIN CATCH
DECLARE #dummy int
SET #dummy = 1
END CATCH

Bulk Update Email Trigger

I created a update trigger in Microsoft SQL Server that would email me if a date changed in a row.
Similar to this:
IF UPDATE(ColumnName)
BEGIN
DECLARE #columnVal AS DATETIME
SELECT
#columnVal = i.columnName
FROM
inserted i JOIN deleted d on i.RowId= d.RowId;
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'me',
#recipients = 'me#company.com',
#body = 'blah blah datechange',
#body_format = 'HTML',
#subject = 'subject';
END
it worked fine for a time.
Then I switched over to batch updates and only the first row email is sent out if the date changes on multiple rows. I tried to set up a cursor to roll through the changes but I cannot get it to work, Similar to below:
DECLARE #columnVal AS DATETIME
DECLARE cur CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
i.ColumnName
FROM
inserted i JOIN deleted d on i.RowId= d.RowId;
OPEN cur
FETCH NEXT FROM cur INTO #columnVal
WHILE ##FETCH_STATUS = 0
BEGIN
IF UPDATE(ColumnName)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'me',
#recipients = 'me#company.com',
#body = 'blah blah datechange',
#body_format = 'HTML',
#subject = 'subject';
END
FETCH NEXT FROM cur INTO #columnVal
END
CLOSE cur
DEALLOCATE cur
Any ideas on how to accomplish this task? Would the Update(ColumnName) function even work properly nested inside a cursor (would it actually tell me if that column was updated for that row?)
A cursor inside a trigger is a very very bad idea.
A trigger should be very lean - it shouldn't do a lot of work! I would recommend to only "take a note" of who you have to send an e-mail to - but leave the actual sending of the e-mail to a separate e.g. SQL Server Agent Job which is not part of the trigger.
Triggers are fired often and often unexpectedly - don't put a log of processing burden into them! And especially not a performance killer like a cursor!
To find those rows that you're interested in, you can use a WHERE clause something like:
WHERE inserted.ColumnName <> deleted.ColumnName
In the context of an UPDATE trigger, this means the new value of ColumnName is different from the old value --> this column has been updated.

Can't drop trigger

I created a trigger on a table, and now I can't alter or delete it.
I cant even access the table.
what can I do?
trigger script:
create trigger [Products].[InsertLessThen5InStock]
on [Products].[Products]
after update
as
BEGIN
DECLARE #ck int
select UnitsInStock from Products.Products where UnitsInStock<5
set #ck=##ROWCOUNT
print #ck
if #ck >0
BEGIN
DECLARE #mails varchar (200)
exec dbo.Manager_email #mails output
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'DefaultMailSender',
#recipients = #mails ,
#body = 'Products that will expire in less then 5 days',
#subject = 'Products that will expire in less then 5 days',
#body_format = 'HTML',
#query = 'EXECUTE MadCat.dbo.SaveTableAsHTML
#DBFetch =''select UnitsInStock from Products.Products where UnitsInStock<5'''
END
END
GO
Since it is complaining about not being able to get a lock, it means some process has reserved the table exclusively. You should try restarting the server, and then issuing the drop trigger command.
drop trigger [Products].[InsertLessThen5InStock]

How to clean sys.conversation_endpoints

I have a table, a trigger on the table implemented using service broker. More than Half million records are inserted daily into the table.
The asynchronous SP is used to check sveral condition by using inserted data and update other tables. It was running fine for last 1 month and the SP was get executed withing 2-3 seconds of insertion of record. But now it take more than 90 minute.
At present sys.conversation_endpoints have too much records.
(Note that all the table are truncated daily as I do not need those records day after)
Other database activities are normal (average 60% CPU Utilization).
Now where i need to look??
I can re-create database without any problem but i don't think it is a good way to resolve the problem
you can 'end conversation #handle' or 'end conversation #handle with cleanup'
Script to end all conversations for a particular service:
DECLARE #sql NVARCHAR(MAX)
,#far_service NVARCHAR(256) = N'far_service_name';
WHILE EXISTS (
SELECT COUNT(*)
FROM sys.conversation_endpoints
WHERE far_service = #far_service
)
BEGIN
SET #sql = N'';
SELECT TOP 1000 #sql = #sql + N'END CONVERSATION ''' + CAST(conversation_handle AS NVARCHAR(50)) + N''' WITH CLEANUP;
'
FROM sys.conversation_endpoints
WHERE far_service = #far_service;
--PRINT #sql;
EXEC sys.sp_executesql #stmt = #sql;
--RETURN;
END;
GO
Here is my solution, after modifying code from #thesqldev
USE [MY_DATABASE_NAME_HERE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[mqConversationsClearAll]
AS
-- Note: you can check the queue by running this query
-- SELECT * FROM sys.conversation_endpoints GO
DECLARE #getid CURSOR
,#sql NVARCHAR(MAX)
,#conv_id NVARCHAR(100)
,#conv_handle NVARCHAR(100)
,#conv_service NVARCHAR(100)
-- ,#far_service NVARCHAR(256) = N'One_Specific_Service_Target';
-- want to create and execute a chain of statements like this, one per conversation
-- END CONVERSATION 'FE851F37-218C-EA11-B698-4CCC6AD00AE9' WITH CLEANUP;
-- END CONVERSATION 'A4B4F603-208C-EA11-B698-4CCC6AD00AE9' WITH CLEANUP;
SET #getid = CURSOR FOR
SELECT [conversation_id], [conversation_handle], [far_service]
FROM sys.conversation_endpoints
WHERE NOT([State] = 'CO')
-- CO = CONVERSING [State]
-- alternatively, might like one service name only
-- WHERE far_service = #far_service
OPEN #getid
FETCH NEXT
FROM #getid INTO #conv_id, #conv_handle, #conv_service
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'END CONVERSATION ' + char(39) + #conv_handle + char(39) + ' WITH CLEANUP;'
EXEC sys.sp_executesql #stmt = #sql;
FETCH NEXT
FROM #getid INTO #conv_id, #conv_handle, #conv_service
END
CLOSE #getid
DEALLOCATE #getid
Then you can execute the newly created stored procedure "mqConversationsClearAll" whenever you want to clear sys.conversation_endpoints