Why does my trigger not want to send the e-mail - sql-server

Good day,
I am new to SQL and creating triggers and I have read up on it and just created a test trigger so as to gain a understanding of how to create one.
I wrote the following Test Trigger and when debugging it, it went through successfully.
USE [Martin_test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Test_to_write_a_trigger]
ON [dbo].[_rtblAgents]
AFTER DELETE, INSERT, UPDATE
AS
BEGIN
EXEC msdb.dbo.sp_send_dbmail
exec msdb.dbo.sp_send_dbmail
#profile_name = 'ChamgeContactMail',
#recipients = 'martin#aboutit.co.za',
#body = 'Someone changed the customer',
#subject = 'Change Customer';
END
However, when I make a change in the specified table, I get the following error.
The SQL Server is residing on my local machine.
Please could someone assist me.

Related

is there a way i can get an alert whenever a table gets records in sql server

i have a table in SQl server which occasionally gets data from a linked server, and than i have to do activities on it .
but the problem is there is no way to check if the data is inserted in table (table is always truncated after performing the activity so next time when data is pushed table is already empty) i manually check daily for data if it is inserted or not .
what i want is to get auto alert on my email (i already have db_mail configured and working) whenever the data is pushed in a table .
i have sa admin and complete privileges on Database and also on Windows server 2012 R2
You can do this with a trigger but you will have to do some preparations with privileges so the executor (the login that's inserting the records on your tracking table) can send email correctly:
CREATE TRIGGER dbo.TrackingTableNameAfterInsert ON TrackingTable
AFTER INSERT
AS
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'YourConfiguredProfile',
#recipients = 'youremail#mail.com',
#subject = 'Records were inserted on TrackingTable',
#body = ''
END
You might want to encapsulate the email sending on an SP and configure it's permissions there.
In regards to the following:
...table is always truncated after performing the activity so next time
when data is pushed table is already empty...
You can create a historical table and use a trigger to also insert inserted records on this table, so the TRUNCATE or DROP of the original one won't affect the copied records.
CREATE TABLE TrackingTableMirror (
/*Same columns and types*/
InsertedDate DATETIME DEFAULT GETDATE())
GO
CREATE TRIGGER dbo.TrackingTableInsertMirror ON TrackingTable
AFTER INSERT
AS
BEGIN
INSERT INTO TrackingTableMirror (
/*Column list*/)
SELECT
/*Column list*/
FROM
inserted AS I
END
This way you can check all records on this mirrored table and not the volatile one (and avoid all the email sending).
1) Create Profile and Account
You need to create a profile and account using the Configure Database Mail Wizard which can be accessed from the Configure Database Mail context menu of the Database Mail node in Management Node. This wizard is used to manage accounts, profiles, and Database Mail global settings.
2) Run Query
sp_CONFIGURE 'show advanced', 1
GO
RECONFIGURE
GO
sp_CONFIGURE 'Database Mail XPs', 1
GO
RECONFIGURE
GO
3)
USE msdb
GO
EXEC sp_send_dbmail #profile_name='yourprofilename',
#recipients='test#Example.com',
#subject='Test message',
#body='This is the body of the test message.
Congrates Database Mail Received By you Successfully.'
through the table
DECLARE #email_id NVARCHAR(450), #id BIGINT, #max_id BIGINT, #query NVARCHAR(1000)
SELECT #id=MIN(id), #max_id=MAX(id) FROM [email_adresses]
WHILE #id<=#max_id
BEGIN
SELECT #email_id=email_id
FROM [email_adresses]
set #query='sp_send_dbmail #profile_name=''yourprofilename'',
#recipients='''+#email_id+''',
#subject=''Test message'',
#body=''This is the body of the test message.
Congrates Database Mail Received By you Successfully.'''
EXEC #query
SELECT #id=MIN(id) FROM [email_adresses] where id>#id
END
4) Trigger Code
CREATE TRIGGER [dbo].[Customer_INSERT_Notification]
ON [dbo].[Customers]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #CustomerId INT
SELECT #CustomerId = INSERTED.CustomerId
FROM INSERTED
declare #body varchar(500) = 'Customer with ID: ' + CAST(#CustomerId AS VARCHAR(5)) + ' inserted.'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Email_Profile'
,#recipients = 'recipient#gmail.com'
,#subject = 'New Customer Record'
,#body = #body
,#importance ='HIGH'
END
I refer this link.

SQL Server 2012 - Trigger failing to commit data locally despite try-catch for sp_testlinkedserver

If I make a change to MyTable on MyDatabase, I want it to delete the contents of TestTable on TestDatabase on MYLINKEDSERVER and reinsert everything from scratch. To do this, I use a trigger on MyTable. I know this is inefficient but the number of records is less than 10.
In case the linked server is down, I want it to still commit the changes locally to MyTable and just send an email alert stating that the linkedserver could not be updated. I am failing to get the local transaction to commit when the linkedserver is unavailable... I've tried messing with XACT_ABORT but I just get different errors.
What am I doing wrong?
CREATE TRIGGER trig_updatelinkedserver ON MyDatabase.dbo.MyTable
FOR INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE #linked_server SYSNAME = 'MYLINKEDSERVER'
, #tablename SYSNAME --name of the table calling the trigger so we can send error details in alert eamil
SELECT #tablename = OBJECT_NAME(parent_object_id)
FROM sys.objects
WHERE sys.objects.name = OBJECT_NAME(##PROCID)
BEGIN TRY
--If linkedserver fails to connect, we do not want the remaining code in the block to run
--but we do want the original transaction that triggered this to complete.
EXEC sp_testlinkedserver #servername = #linked_server
DELETE FROM MYLINKEDSERVER.TestDatabase.dbo.TestTable
INSERT INTO MYLINKEDSERVER.TestDatabase.dbo.TestTable
SELECT *
FROM MyDatabase.dbo.MyTable
END TRY
BEGIN CATCH
DECLARE #subj VARCHAR(1000) = 'TRIGGER FAILURE: ' + #tablename + ': Could not locate linkedserver ' + #linked_server
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'foo#bar.com'
, #subject = #subj
, #body = ''
, #body_format = 'HTML'
, #profile_name = 'MyEmailProfile'
END CATCH
END
Error handling in tsql is complicated and inconsistent. What are you doing wrong? Making assumptions. Here is what Erland says in his lengthy discussion about the topic:
What is important to understand about triggers is that they are part of the command that fired the trigger, and in a trigger you are always in a transaction, even if you did not use BEGIN TRANSACTION. Sometimes I see people in SQL Server forums ask if they can write a trigger that does not roll back the command that fired the trigger if the trigger fails. The answer is that there is no way that you can do this reliably, so you better not even try. If you have this type of requirement, you should probably not use a trigger at all, but use some other solution.
So take that last sentence to heart. Note that the link takes you into the middle of the discussion. And there are links at the end of the page that continue to related topics - one of which is about linked servers.

SQL Server Trigger fires unexplainably

I've got a trigger on a table. It's a very simple trigger, set to after insert, send me an e-mail. Since I've put that trigger on, I've been sent e-mails by the system every 5 or 6 minutes or so. There is just one problem.
Whenever I receive an e-mail, the table is EMPTY
Here is my trigger
USE [didaprod]
GO
/****** Object: Trigger [dbo].[Caseplayer_CaseId_Restore_insert_mail] Script Date: 09-08-2016 11:59:41 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE trigger [dbo].[Caseplayer_CaseId_Restore_insert_mail] on [dbo].[Caseplayer_CaseId_Restore]
AFTER INSERT
as
SET NOCOUNT ON
declare #tekst nvarchar(500);
set #tekst = 'caseid sat til null på caseplayer! Tjek Caseplayer_CaseId_Restore tabel!' + convert(varchar,getdate(),105);
EXEC msdb.dbo.sp_send_dbmail #profile_name = 'Mail',
#recipients = 'kk#byggeevaluering.dk',
#subject = 'CASEID SAT TIL NULL!!!',
#body = #tekst
SET NOCOUNT OFF
GO
I've tried to manually insert a row, or a couple of rows in the table, just to check, and yes, the trigger fires as well, when there is a proper insert. But I cannot explain why I keep receiving the e-mails!. As for the table itself, it's got nothing fancy.
USE [didaprod]
GO
/****** Object: Table [dbo].[Caseplayer_CaseId_Restore] Script Date: 09-08-2016 12:04:46 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Caseplayer_CaseId_Restore](
[Id] [int] NOT NULL,
[CaseId] [int] NOT NULL,
[TimeOfChange] [datetime] NOT NULL
) ON [PRIMARY]
GO
I've disabled the trigger now, and confirmed that the e-mails have stopped. But this seems like an almost magic problem for me, and I would love to get an answer.
Suppose I do
delete from [Caseplayer_CaseId_Restore]
And just leave the table alone.
I'll still get e-mails from the system, telling me to check the table. When I then perform a
select * from [Caseplayer_CaseId_Restore]
The table is empty
SQL Server triggers fire once per statement rather than once per row. That's why it's important to write triggers that use inserted appropriately, to deal with the fact that it may contain 1, many, or zero rows.
I suspect that it's the latter case here. (I.e. a regularly executed INSERT statement that is in fact inserting zero rows)
So, you might want something like:
CREATE trigger [dbo].[Caseplayer_CaseId_Restore_insert_mail]
on [dbo].[Caseplayer_CaseId_Restore]
AFTER INSERT
as
SET NOCOUNT ON
IF EXISTS(select * from inserted)
BEGIN
declare #tekst nvarchar(500);
set #tekst = 'caseid sat til null på caseplayer! Tjek Caseplayer_CaseId_Restore tabel!' + convert(varchar,getdate(),105);
EXEC msdb.dbo.sp_send_dbmail #profile_name = 'Mail',
#recipients = 'kk#byggeevaluering.dk',
#subject = 'CASEID SAT TIL NULL!!!',
#body = #tekst
END

Signal from Database

I get a form filled from the user through browser..
I enter the values in a database
I want to call a script on the entry made in the database
(Do not want to call it from browser as browser has timeout issues. This script can take 10 hrs if it needs to)
Looks like you need a trigger:
A trigger is a special kind of stored procedure that automatically executes when an event occurs in the database server.
Example:
USE AdventureWorks2012;
GO
IF OBJECT_ID ('Sales.reminder2','TR') IS NOT NULL
DROP TRIGGER Sales.reminder2;
GO
CREATE TRIGGER reminder2
ON Sales.Customer
AFTER INSERT, UPDATE, DELETE
AS
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'AdventureWorks2012 Administrator',
#recipients = 'danw#Adventure-Works.com',
#body = 'Don''t forget to print a report for the sales force.',
#subject = 'Reminder';
GO
CREATE TRIGGER (Transact-SQL)

SQL Server 2008 insert trigger not firing

I have an INSERT trigger on a table that simply executes a job.
Example:
CREATE TABLE test
(
RunDate smalldatetime
)
CREATE TRIGGER StartJob ON test
AFTER INSERT
AS
EXEC msdb.dbo.sp_start_job 'TestJob'
When I insert a record to this table, the job is fired of without any issue. There are a few people, however, that have lower permissions than I do (db_datareader/db_datawriter on the database only); they are able to insert a record to the table, but the trigger does not fire.
I am a SQL Server novice and I was under the impression that users did not need elevated permissions to fire off a trigger (I thought that was one of the big benefits!). Is this a permission issue at the trigger level, or at the job level? What can I do to get around this limitation?
The trigger will execute in the context of the caller, which may or may not have the permissions to access msdb. That seems to be your problem. There are a few ways to extend these permissions using Execute As; they are greatly detailed in this link
Use impersonation within trigger:
CREATE TRIGGER StartJob ON test
with execute as owner
AFTER INSERT
AS
EXEC msdb.dbo.sp_start_job 'TestJob'
And set database to trustworthy (or read about signing in above link):
alter database TestDB set trustworthy on
Another way to go (depending on what operations the agent job performs) would be to leverage a Service Broker queue to handle the stored procedure activation. Your users' context would simply call to Send On the queue while, in an asynchronous process SvcBroker would activate a stored procedure which executed in context of higher elevated user. I would opt for this solution rather than relying on a trigger calling an agent job.
I wanted to test the call to Service Broker, so I wrote this simple test example. Instead of calling an SSIS package I simply send an email, but it is very similar to your situation. Notice I use SET TRUSTWORTHY ON at the top of the script. Please read about the implications of this setting.
To run this sample you will need to substitute your email profile info below, <your_email_address_here>, etc.
use Master;
go
if exists(select * from sys.databases where name = 'TestDB')
drop database TestDB;
create database TestDB;
go
alter database TestDB set ENABLE_BROKER;
go
alter database TestDB set TRUSTWORTHY ON;
use TestDB;
go
------------------------------------------------------------------------------------
-- create procedure that will be called by svc broker
------------------------------------------------------------------------------------
create procedure dbo.usp_SSISCaller
as
set nocount on;
declare #dlgid uniqueidentifier;
begin try
-- * figure out how to start SSIS package from here
-- for now, just send an email to illustrate the async callback
;receive top(1)
#dlgid = conversation_handle
from SSISCallerQueue;
if ##rowcount = 0
begin
return;
end
end conversation #dlgid;
exec msdb.dbo.sp_send_dbmail
#profile_name = '<your_profile_here>',
#importance = 'NORMAL',
#sensitivity = 'NORMAL',
#recipients = '<your_email_address_here>',
#copy_recipients = '',
#blind_copy_recipients = '',
#subject = 'test from ssis caller',
#body = 'testing',
#body_format = 'TEXT';
return 0;
end try
begin catch
declare #msg varchar(max);
select #msg = error_message();
raiserror(#msg, 16, 1);
return -1;
end catch;
go
------------------------------------------------------------------------------------
-- setup svcbroker objects
------------------------------------------------------------------------------------
create contract [//SSISCallerContract]
([http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer] sent by initiator)
create queue SSISCallerQueue
with status = on,
activation (
procedure_name = usp_SSISCaller,
max_queue_readers = 1,
execute as 'dbo' );
create service [//SSISCallerService]
authorization dbo
on queue SSISCallerQueue ([//SSISCallerContract]);
go
return;
-- usage
/*
-- put a row into the queue to trigger the call to usp_SSISCaller
begin transaction;
declare #dlgId uniqueidentifier;
begin dialog conversation #dlgId
from service [//SSISCallerService]
to service '//SSISCallerService',
'CURRENT DATABASE'
on contract [//SSISCallerContract]
with encryption = off;
begin conversation timer (#dlgId)
TIMEOUT = 5; -- seconds
commit transaction;
*/
It would be permissions at the job level. You can possibly assign those users the SQLAgentReaderRole in MSDB to be able to start a job, considering that they would be added to a group that owned the job. If they are not in a group which owns the job, it gets more difficult.

Resources