我有两张这样的桌子:
工作表
Jobcode UserId Status
101 130 R
102 139 D
用户表
UserId Email
130 test@example.com
我想在插入和更新时创建一个触发器,将电子邮件发送到我的存储过程:
EXEC dbo.SendMyEmail @email, @jobcode;
当作业代码作为“D”插入或更新为“D”时。
我有两张这样的桌子:
工作表
Jobcode UserId Status
101 130 R
102 139 D
用户表
UserId Email
130 test@example.com
我想在插入和更新时创建一个触发器,将电子邮件发送到我的存储过程:
EXEC dbo.SendMyEmail @email, @jobcode;
当作业代码作为“D”插入或更新为“D”时。
在我看来,在触发器中发送电子邮件并不是最佳选择。
相反,您应该只插入到队列表中,并让一个进程经常运行来检查表并发送电子邮件。
如果您在电子邮件过程中遇到错误会怎样?它将强制回滚您的作业完成状态。只有你知道那是次要的还是可能是灾难性的。但我可以肯定地告诉你,数据库的最佳实践是在 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 上检查任何内容就输入了以上所有内容。很可能有错别字。请原谅任何人。
我不确定数据类型等,但这至少应该让你走上正轨。希望能帮助到你...
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
此外,正如评论中所讨论的,从触发器发送电子邮件也不是最好的,但因为这是问题所要求的,所以已包含在内。我会推荐发送电子邮件的替代选项,例如其他答案中提到的队列。