0

我有两张这样的桌子:

工作

Jobcode    UserId    Status
101        130       R
102        139       D

用户

UserId    Email
130       test@example.com

我想在插入和更新时创建一个触发器,将电子邮件发送到我的存储过程:

EXEC dbo.SendMyEmail @email, @jobcode;

当作业代码作为“D”插入或更新为“D”时。

4

2 回答 2

3

在我看来,在触发器中发送电子邮件并不是最佳选择。

相反,您应该只插入到队列表中,并让一个进程经常运行来检查表并发送电子邮件。

如果您在电子邮件过程中遇到错误会怎样?它将强制回滚您的作业完成状态。只有你知道那是次要的还是可能是灾难性的。但我可以肯定地告诉你,数据库的最佳实践是在 DML 操作期间不进行扩展 I/O。

CREATE TRIGGER TR_Jobs_EnqueueEmail_IU ON dbo.Jobs FOR INSERT, UPDATE
AS
SET NOCOUNT ON;
INSERT dbo.EmailQueue (UserID, JobCode)
SELECT UserID, JobCode
FROM
   Inserted I
   LEFT JOIN Deleted D
      ON I.JobCode = D.JobCode -- or proper PK columns
WHERE
   IsNull(D.Status, 'R') <> 'D'
   AND I.Status = 'D';

需要的表:

CREATE TABLE dbo.EmailQueue (
   QueuedDate datetime NOT NULL
      CONSTRAINT DF_EmailQueue_QeueueDate DEFAULT (GetDate()),
   UserID int NOT NULL,
   JobCode int NOT NULL,
   CONSTRAINT PK_EmailQueue PRIMARY KEY CLUSTERED (QueuedDate, UserID, JobCode)
);

CREATE TABLE dbo.EmailSent (
   SentDate datetime NOT NULL
      CONSTRAINT DF_EmailSent_SentDate DEFAULT (GetDate()),
   QueuedDate datetime NOT NULL,
   UserID int NOT NULL,
   JobCode int NOT NULL,
   CONSTRAINT PK_EmailSent PRIMARY KEY CLUSTERED (SentDate, QueuedDate, UserID, JobCode)
);

然后,从 SQL 作业中每分钟运行一次以下存储过程:

CREATE PROCEDURE dbo.EmailProcess
AS
DECLARE @Email TABLE (
   QueuedDate datetime,
   UserID int,
   JobCode int
);
DECLARE
   @EmailAddress nvarchar(255),
   @JobCode int;

WHILE 1 = 1 BEGIN

   DELETE TOP 1 Q.*
   OUTPUT Inserted.QueuedDate, Inserted.UserID, Inserted.JobCode
   INTO @Email (QueuedDate, UserID, JobCode)
   FROM dbo.EmailQueue Q WITH (UPDLOCK, ROWLOCK, READPAST)
   ORDER BY QueuedDate;

   IF @@RowCount = 0 RETURN;

   SELECT @EmailAddress = U.EmailAddress, @JobCode = E.JobCode
   FROM
      @Email E
      INNER JOIN dbo.User U
         ON E.UserID = U.UserID;

   EXEC dbo.SendMyEmail @EmailAddress, @JobCode;

   DELETE E
   OUTPUT QueuedDate, UserID, JobCode
   INTO dbo.EmailSent (QueuedDate, UserID, JobCode)
   FROM @Email E;

END;

我使用的删除模式和锁是经过特别选择的。如果您更改它们或以任何方式更改删除模式,几乎可以肯定您会破坏它。处理锁和并发是困难的。不要改变它。

注意:我没有在 SQL Server 上检查任何内容就输入了以上所有内容。很可能有错别字。请原谅任何人。

于 2012-08-03T05:26:22.597 回答
2

我不确定数据类型等,但这至少应该让你走上正轨。希望能帮助到你...

CREATE TRIGGER SendEmailOnStatusD

ON JOBS

-- trigger is fired when an update is made for the table
FOR UPDATE --You can add the same for INSERT

AS

    -- holds the UserID so we know which Customer was updated
    DECLARE @UserID int
    DECLARE @JobCode int

    SELECT @UserID = UserId, @JobCode = JobCode 
    FROM INSERTED WHERE [Status] = 'D' --If you want the old value before the update, use 'deleted' table instead of 'inserted' table

    IF (@UserID IS NOT NULL)
    BEGIN

        -- holds the email
        DECLARE @email varchar(250)

        SELECT @email = Email FROM USERS WHERE UserId = @UserID

        EXEC SendMyEmail (@email, @jobcode);

    END

GO

编辑

上面的代码不处理多个更新,所以为了更好的实践,请参见下面的选项

CREATE TRIGGER SendEmailOnStatusD ON JOBS

-- trigger is fired when an update is made for the table
FOR UPDATE --You can add the same for INSERT

AS

    DECLARE @Updates table(UserID int, JobCode int, Email varchar(250))

    INSERT INTO @Updates (UserID, JobCode, Email)
    SELECT i.UserID, i.JobCode, u.Email
    FROM INSERTED i
        JOIN USERS u ON i.UserID = u.UserID
    WHERE [Status] = 'D'

    DECLARE @UserID int
    DECLARE @JobCode int
    DECLARE @Email varchar(250)

    WHILE EXISTS(SELECT * FROM @Updates)
    BEGIN

        SELECT TOP 1
            @UserID = UserID,
            @Email = Email, 
            @JobCode = JobCode
        FROM @Updates WHERE UserID = @UserID

        EXEC SendMyEmail (@email, @jobcode);

        DELETE FROM @Updates
        WHERE UserID = @UserID

    END

GO

此外,正如评论中所讨论的,从触发器发送电子邮件也不是最好的,但因为这是问题所要求的,所以已包含在内。我会推荐发送电子邮件的替代选项,例如其他答案中提到的队列。

于 2012-08-02T22:33:32.023 回答