我有2个这样的表:
JOBS 表
Jobcode UserId Status
101 130 R
102 139 D
USERS 表
UserId Email
130 test@example.com
我想在插入和更新时创建一个触发器,它会向我的存储过程发送一封电子邮件:
EXEC dbo.SendMyEmail @email, @jobcode;
当作业代码插入为“D”或更新为“D”时。
答案 0 :(得分:3)
在我看来,在触发器中发送电子邮件并不是最佳选择。
相反,您应该只是插入到队列表中,并且经常运行一个检查表并发送电子邮件的进程。
如果您的电子邮件程序出错,会发生什么?它将强制回滚您的作业完成状态。只有你知道这是次要的还是可能是灾难性的。但我可以肯定地告诉你,DB最佳做法是在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;
我特别选择了我使用的删除模式和锁。如果您更改它们或以任何方式更改删除模式 ,几乎可以肯定您会破坏它。处理锁和并发是 hard 。不要改变它。
注意:我在不检查SQL Server上的任何内容的情况下键入了以上所有内容。可能存在拼写错误。请原谅任何人。
答案 1 :(得分: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
此外,正如评论中所讨论的那样,从触发器发送电子邮件也不是最好的,但正如这个问题所要求的那样。我建议使用其他选项来发送电子邮件,例如其他答案中提到的队列。