尝试让触发器发送电子邮件时 SQL 触发器不起作用

发布于 2024-10-17 23:37:57 字数 697 浏览 1 评论 0原文

我正在尝试创建一个数据库触发器,当插入具有特定单词的新行时会触发该触发器,它将发送电子邮件警报。当我只运行该代码时,电子邮件警报就会起作用,如果我删除电子邮件片段并将其替换为其他内容,则触发器就会起作用。

这是代码:

ALTER TRIGGER CheckForDirty
ON dbo.Messages
FOR INSERT,UPDATE
AS

DECLARE @MESSAGETEXT varchar(max)
DECLARE @BADWORD1 varchar(50)

SET @MESSAGETEXT = (SELECT Body FROM inserted)
SET @BADWORD1 = 'BadWord'

IF CHARINDEX(@BADWORD1, @MESSAGETEXT) > 0
BEGIN

    EXEC msdb.dbo.sp_send_dbmail @recipients='[email protected]',
    @subject = 'Test e-mail sent from database mail',
    @body = 'Someone said something dirty'

END
GO

I am trying to create a database trigger that fires off when a new row is inserted which has a specific word it will send an email alert. The email alert works when I just run that code and the trigger works if I remove the email piece and replace it with something else.

Here is the code:

ALTER TRIGGER CheckForDirty
ON dbo.Messages
FOR INSERT,UPDATE
AS

DECLARE @MESSAGETEXT varchar(max)
DECLARE @BADWORD1 varchar(50)

SET @MESSAGETEXT = (SELECT Body FROM inserted)
SET @BADWORD1 = 'BadWord'

IF CHARINDEX(@BADWORD1, @MESSAGETEXT) > 0
BEGIN

    EXEC msdb.dbo.sp_send_dbmail @recipients='[email protected]',
    @subject = 'Test e-mail sent from database mail',
    @body = 'Someone said something dirty'

END
GO

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

埖埖迣鎅 2024-10-24 23:37:57

强烈建议您将电子邮件发送代码与触发器分离 - 在触发器中,将所需的信息插入“待处理电子邮件”表中。然后创建一个定期检查表并发送电子邮件的作业。

您还需要重写触发器以应对多行插入和更新。 inserted 是一个可以包含多行的伪表。

所以我会编写一个像这样的触发器:

CREATE TRIGGER CheckForDirty
ON dbo.Messages
FOR INSERT,UPDATE
AS

INSERT INTO PendingEmails (Recipients,Subject,Body)
SELECT '[email protected]','Test e-mail sent from database mail','Someone said something dirty'
FROM inserted
WHERE CHARINDEX('BadWord',Body) > 0

尽管我假设您正在构造的消息的实际正文可能依赖于更多信息来帮助识别涉及哪条消息。


好的,如果我们要将其构建为一个完整的示例,我们需要定义 PendingEmails:

CREATE TABLE PendingEmails (
    PendingEmailID int IDENTITY(1,1) not null,
    Recipients varchar(max) not null,
    Subject nvarchar(255) not null,
    Body nvarchar(max) not null,
    constraint PK_PendingEmails PRIMARY KEY (PendingEmailID)
)

现在我们将编写一个可以处理待处理电子邮件的存储过程:

CREATE PROCEDURE DispatchPendingEmails
AS
     declare @PendingEmailID int
     declare @Recipients varchar(max)
     declare @Subject nvarchar(255)
     declare @Body nvarchar(max)

     while exists(select * from PendingEmails)
     begin
         select top 1 @PendingEmailID = PendingEmailID,@Recipients = Recipients,
             @Subject = Subject, @Body = Body from PendingEmails

         exec msdb.dbo.sp_send_dbmail @recipients = @Recipients, @subject = @Subject, @body = @Body

         delete from PendingEmails where PendingEmailID = @PendingEmailID
     end

最后,我们需要创建作业。我通常通过 SSMS 执行此操作,但如果您希望将其编写为脚本(您需要在 sp_add_jobstep 行中替换不同的数据库名称,并且设置为每五分钟运行一次调度。所有存储过程都很好MSDN 中记录):

USE [msdb]
GO
DECLARE @jobId BINARY(16)
EXEC  msdb.dbo.sp_add_job @job_name=N'DispatchPendingEmails', 
        @enabled=1, 
        @notify_level_eventlog=0, 
        @notify_level_email=2, 
        @notify_level_netsend=2, 
        @notify_level_page=2, 
        @delete_level=0, 
        @category_name=N'[Uncategorized (Local)]', 
        @owner_login_name=N'sa', @job_id = @jobId OUTPUT
select @jobId
GO
EXEC msdb.dbo.sp_add_jobserver @job_name=N'DispatchPendingEmails', @server_name = N'SYSTEMS86\SQL2K8'
GO
USE [msdb]
GO
EXEC msdb.dbo.sp_add_jobstep @job_name=N'DispatchPendingEmails', @step_name=N'ExecSP', 
        @step_id=1, 
        @cmdexec_success_code=0, 
        @on_success_action=1, 
        @on_fail_action=2, 
        @retry_attempts=0, 
        @retry_interval=0, 
        @os_run_priority=0, @subsystem=N'TSQL', 
        @command=N'exec DispatchPendingEmails', 
        @database_name=N'Database', --<-- This needs editing
        @flags=0
GO
USE [msdb]
GO
EXEC msdb.dbo.sp_update_job @job_name=N'DispatchPendingEmails', 
        @enabled=1, 
        @start_step_id=1, 
        @notify_level_eventlog=0, 
        @notify_level_email=2, 
        @notify_level_netsend=2, 
        @notify_level_page=2, 
        @delete_level=0, 
        @description=N'', 
        @category_name=N'[Uncategorized (Local)]', 
        @owner_login_name=N'sa', 
        @notify_email_operator_name=N'', 
        @notify_netsend_operator_name=N'', 
        @notify_page_operator_name=N''
GO
USE [msdb]
GO
DECLARE @schedule_id int
EXEC msdb.dbo.sp_add_jobschedule @job_name=N'DispatchPendingEmails', @name=N'QuiteOften', 
        @enabled=1, 
        @freq_type=4, 
        @freq_interval=1, 
        @freq_subday_type=4, 
        @freq_subday_interval=5, 
        @freq_relative_interval=0, 
        @freq_recurrence_factor=1, 
        @active_start_date=20110218, 
        @active_end_date=99991231, 
        @active_start_time=0, 
        @active_end_time=235959, @schedule_id = @schedule_id OUTPUT
select @schedule_id
GO

You would be well advised to decouple the email sending code from the trigger - in the trigger, insert the information you need into a "Pending Emails" table. Then create a job that periodically checks the table and send email.

You also need to re-write your trigger to cope with multi-row inserts and updates. inserted is a pseudo table that can contain multiple rows.

So I'd write a trigger something like this:

CREATE TRIGGER CheckForDirty
ON dbo.Messages
FOR INSERT,UPDATE
AS

INSERT INTO PendingEmails (Recipients,Subject,Body)
SELECT '[email protected]','Test e-mail sent from database mail','Someone said something dirty'
FROM inserted
WHERE CHARINDEX('BadWord',Body) > 0

Although I'm assuming that the actual body of the message you're constructing may rely on more info to help identify which message is implicated.


Okay, if we're going to build this out into a full example, we'll need to define PendingEmails:

CREATE TABLE PendingEmails (
    PendingEmailID int IDENTITY(1,1) not null,
    Recipients varchar(max) not null,
    Subject nvarchar(255) not null,
    Body nvarchar(max) not null,
    constraint PK_PendingEmails PRIMARY KEY (PendingEmailID)
)

And now we'll write a stored proc that can process pending emails:

CREATE PROCEDURE DispatchPendingEmails
AS
     declare @PendingEmailID int
     declare @Recipients varchar(max)
     declare @Subject nvarchar(255)
     declare @Body nvarchar(max)

     while exists(select * from PendingEmails)
     begin
         select top 1 @PendingEmailID = PendingEmailID,@Recipients = Recipients,
             @Subject = Subject, @Body = Body from PendingEmails

         exec msdb.dbo.sp_send_dbmail @recipients = @Recipients, @subject = @Subject, @body = @Body

         delete from PendingEmails where PendingEmailID = @PendingEmailID
     end

And finally, we need to create the job. I'd normally do this through SSMS, but if you want it scripted (you'll need to substitute a different database name in the sp_add_jobstep line, and this is set to run the dispatch every five minutes. All of the stored procs are well documented in MSDN):

USE [msdb]
GO
DECLARE @jobId BINARY(16)
EXEC  msdb.dbo.sp_add_job @job_name=N'DispatchPendingEmails', 
        @enabled=1, 
        @notify_level_eventlog=0, 
        @notify_level_email=2, 
        @notify_level_netsend=2, 
        @notify_level_page=2, 
        @delete_level=0, 
        @category_name=N'[Uncategorized (Local)]', 
        @owner_login_name=N'sa', @job_id = @jobId OUTPUT
select @jobId
GO
EXEC msdb.dbo.sp_add_jobserver @job_name=N'DispatchPendingEmails', @server_name = N'SYSTEMS86\SQL2K8'
GO
USE [msdb]
GO
EXEC msdb.dbo.sp_add_jobstep @job_name=N'DispatchPendingEmails', @step_name=N'ExecSP', 
        @step_id=1, 
        @cmdexec_success_code=0, 
        @on_success_action=1, 
        @on_fail_action=2, 
        @retry_attempts=0, 
        @retry_interval=0, 
        @os_run_priority=0, @subsystem=N'TSQL', 
        @command=N'exec DispatchPendingEmails', 
        @database_name=N'Database', --<-- This needs editing
        @flags=0
GO
USE [msdb]
GO
EXEC msdb.dbo.sp_update_job @job_name=N'DispatchPendingEmails', 
        @enabled=1, 
        @start_step_id=1, 
        @notify_level_eventlog=0, 
        @notify_level_email=2, 
        @notify_level_netsend=2, 
        @notify_level_page=2, 
        @delete_level=0, 
        @description=N'', 
        @category_name=N'[Uncategorized (Local)]', 
        @owner_login_name=N'sa', 
        @notify_email_operator_name=N'', 
        @notify_netsend_operator_name=N'', 
        @notify_page_operator_name=N''
GO
USE [msdb]
GO
DECLARE @schedule_id int
EXEC msdb.dbo.sp_add_jobschedule @job_name=N'DispatchPendingEmails', @name=N'QuiteOften', 
        @enabled=1, 
        @freq_type=4, 
        @freq_interval=1, 
        @freq_subday_type=4, 
        @freq_subday_interval=5, 
        @freq_relative_interval=0, 
        @freq_recurrence_factor=1, 
        @active_start_date=20110218, 
        @active_end_date=99991231, 
        @active_start_time=0, 
        @active_end_time=235959, @schedule_id = @schedule_id OUTPUT
select @schedule_id
GO
天涯离梦残月幽梦 2024-10-24 23:37:57

第一个猜测是权限。当您手动运行电子邮件发送时,您是否在与执行触发器时不同的帐户下运行?我之前见过这样的情况,您在用户帐户下测试代码,然后在具有较少权限的不同帐户下运行应用程序。您需要确保所有用户都位于正确的邮件组中。

First guess would be permissions. When you run the email send manually are you running under a different account than when you execute the trigger? I have seen this before where you test the code under your user account then run an application under a different account that has less permissions. You need to make sure all users are in the correct mail group.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文