【问题标题】:Using a cursor in a trigger to change subject of db_sendmail在触发器中使用游标更改 db_sendmail 的主题
【发布时间】:2015-05-18 17:35:36
【问题描述】:

SQl Server 2008 R2 - 我有一个触发器,它设置为向特定的电子邮件地址发送电子邮件,该地址由第三方系统监控,该系统根据该消息的主题向某些人群发送消息。对此的限制是,每条消息的主题中只能有一个组号。但是我需要将相同的消息从触发器发送到至少两个组,这意味着必须使用两个不同的主题行生成两封单独的电子邮件。我在想触发器中的光标可能会完成此操作,但我不确定如何编写它。 因此,在下面的代码中,我有@cat,每次触发器启动时,我都需要在其中发送主题为“ED”的电子邮件。然后,我需要根据当前用于设置主题行的案例语句的条件发送另一封电子邮件,其中主题正在设置。我已按如下方式更新了代码,并通过删除标量变量已移至基于集合的触发器(??)我还创建了信息并将其插入到“电子邮件”表中,该表将设置一个服务来运行每个10 秒并通过电子邮件发送所有设置为 0 的记录,然后将标志更新为 1。

新代码 -

CREATE TABLE [dbo].[tb_BatchEmail] (
    [BatchEmailID] [bit] NOT NULL DEFAULT 0
    ,[To] [varchar](50) NOT NULL DEFAULT 'someemail' --will never change
    ,[Body] [varchar](255) NULL
    ,[Subject] [varchar](20) NOT NULL
    ,[Profile] [varchar](50) NOT NULL DEFAULT 'Alert' --will never change
    ,[OrderID] [varchar] (25) NULL
    ,[OrderDateTime] [datetime] NULL
    ,[SentDateTime] [datetime] NULL
    ,CONSTRAINT msg_pk PRIMARY KEY CLUSTERED (BatchEmailID)
    ) ON [PRIMARY]
GO



  ALTER TRIGGER [dbo].[VeOrders] ON [dbo].[Orders]
        FOR INSERT
        AS
            SELECT @visitid = i.VisitID
    ,@priority = Priority
    ,@cat = Category
    ,@procedure = OrderedProcedureName
    ,@orderid = OrderID
    ,@orderdate = OrderDateTime
    ,@locationid = CurrentLocationID
    ,@roomid = CASE 
        WHEN RoomTreatmentID IS NULL
            THEN 'No Room#'
        ELSE RoomTreatmentID
        END
FROM inserted i
INNER JOIN livedb.dbo.EdmPatients edp
    ON edp.VisitID = i.VisitID
WHERE Priority = 'STAT'
    AND Category IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
    AND CurrentLocationID = 'ED'

IF @cat IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
    AND @priority = 'STAT'
    AND @locationid = 'ED'
BEGIN
    DECLARE @msg VARCHAR(500)
    DECLARE @subject VARCHAR(500)

    SET @msg = @roomid + '-' + @procedure + '-' + @priority + '.' --+ 'Order DateTime/Number ' + @order + '/+ @locationid + ' '  + @orderid
    SET @subject = CASE 
            WHEN @cat = 'US'
                THEN 'Test Ultra Sound'
            WHEN @cat = 'CT'
                THEN 'Test C T'
            WHEN @cat = 'XRAY'
                THEN 'Test X-Ray'
            END

    INSERT INTO livedb.dbo.tb_BatchEmail (
        Body
        ,[Subject]
        ,OrderID
        ,OrderDateTime
        )
    SELECT Body = @msg
        ,[Subject] = @subject
        ,OrderID = @orderid
        ,OrderDateTime = @orderdate

END

旧代码 -

ALTER TRIGGER [dbo].[VeOrders] ON [dbo].[Orders]
FOR INSERT
AS
IF NOT EXISTS (
        SELECT *
        FROM dbo.EdmPatients
        WHERE CurrentLocationID = 'ED'
        )
    RETURN

DECLARE @priority VARCHAR(50)
DECLARE @cat VARCHAR(50)
DECLARE @procedure VARCHAR(50)
DECLARE @orderid VARCHAR(50)
DECLARE @locationid VARCHAR(10)
DECLARE @roomid VARCHAR(10)
DECLARE @visitid VARCHAR(50)

SELECT @visitid = VisitID
    ,@priority = Priority
    ,@cat = Category
    ,@procedure = OrderedProcedureName
    ,@orderid = OrderID
    ,@locationid = CurrentLocationID
    ,@roomid = CASE 
        WHEN RoomTreatmentID IS NULL
            THEN 'No Room#'
        ELSE RoomTreatmentID
        END
FROM inserted i
INNER JOIN dbo.EdmPatients edp
    ON edp.VisitID = i.VisitID
WHERE Priority = 'STAT'
    AND Category IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
    AND CurrentLocationID = 'ED'

IF @cat IN ('CT', 'MRI', 'XRAY', 'US', 'NUC', 'ECHO')
    AND @priority = 'STAT'
    AND @locationid = 'ED'
BEGIN
    DECLARE @msg VARCHAR(500)
    DECLARE @subject VARCHAR(500)

    SET @msg = @roomid + '-' + @procedure + '-' + @priority + '.' 
    SET @subject = CASE
            WHEN @cat = 'CT'
                THEN '55194'
            WHEN @cat = 'US'
                THEN '59843'
            WHEN @cat = 'XRAY'
                THEN '70071'
            END

    EXEC msdb.dbo.sp_send_dbmail 
         @recipients = N'someemail'
        ,@body = @msg
        ,@subject = @subject
        ,@profile_name = 'Alerts'
END

【问题讨论】:

  • 您的触发器存在重大逻辑缺陷。它假定插入时只会有一行。 SQL Server 每次操作执行一次触发器,而不是每行一次。您需要设置基于触发器的触发器,以便它们可以处理多行。
  • 肖恩 - 你能详细说明一下吗?我之前读过基于集合的查询,但我的理解是,当可以一次更新多行时,更新语句更适用。将记录插入另一个表以与@Bill Gregg 等单独的电子邮件服务一起使用是否建议正确?我只是不确定?任何帮助表示赞赏。我应该用这是我第一次尝试触发器来限定我的帖子。
  • 我更喜欢这种类型的东西的保持表的想法。这意味着当您将数据插入表格时,您不必等待发送电子邮件。我不能说它会解决问题,因为如果您使用标量变量,您仍然会遇到多行问题。请记住,插入可以是多行,就像更新一样容易。

标签: sql-server triggers cursor sp-send-dbmail


【解决方案1】:

我会将关注点与创建电子邮件和发送电子邮件分开。拥有一项服务,其唯一工作就是发送电子邮件。电子邮件是抽象的,因为它们都有 From、To、Body 和 Subject。创建一个包含所有这些字段的表(例如名为 BatchEmail)。

那么你的触发器很简单。它只是在普通的 select 语句中插入到您的 BatchEmail 表中。

insert into BatchEmail
(To, Body, Subject, Profile)
select N'someemail',
       @msg,
       CASE
            WHEN @cat = 'CT'
                THEN '55194'
            WHEN @cat = 'US'
                THEN '59843'
            WHEN @cat = 'XRAY'
                THEN '70071'
            END,
        'Alerts'

您的 BatchEmail 表应该有一些列或标志来指示何时发送或是否发送。

您的服务(甚至是表格​​上的插入触发器,如果​​您真的喜欢触发器)一次只能选择 1 行尚未处理。

while true
begin

    select top 1 
           @BatchEmailID = batchEmailID,
           @To = to,
           @From = from,
           @Body = body,
           @Subject = subject,
           @ProfileName = ProfileName
    from BatchEmail
    where processed = 0

    if(@BatchEmailID is null)
    break;

    exec msdb.dbo.sp_send_dbmail @To, @Body, @Subject, @ProfileName

    update BatchEmail
    set processed = 1
    where BatchEmailID = @BatchEmailID

end

如果您仍然不希望有单独的服务并坚持在触发器中执行所有这些操作,您可以将 @BatchEmail 转换为表变量,仍然带有 @Processed 位列并一次选择 1 行,更新@在每一行之后处理。

【讨论】:

  • 那么batchEmail 表会继续增长吗?对于每个新的“触发器”,将在表中插入另外两行或更多行并相应地通过电子邮件发送,然后为所述记录设置 batchEmailID = 1?如果是这种情况,是否有性能原因或其他原因将其保留在触发器中?因为随着时间的推移,batchEmail 表可能会变得非常大,所以我需要添加额外的步骤来在一段时间后删除记录。如果我将它保留在触发器本身中,我就不必担心清理 batchEmail 表了吗?还是我误会了你?
  • 你没有误会我的意思。是的,它们会增长,因此您最终必须清除它们(如果您愿意的话。磁盘空间仍然很便宜。)您的企业可能希望拥有这些数据。就我个人而言,我不喜欢在触发器中使用这样的魔法。触发器不容易被发现,而且每个人都不清楚这些电子邮件是如何发送的。最好有一个每个人都能看到的专用流程。国际海事组织。仅仅因为你可以在触发器中做某事并不意味着你应该这样做。
  • 当我想到另一个潜在问题时,我一直在按照您的建议进行设置。我需要在数据库可以做到的情况下近乎实时地或尽可能接近实时地发送这些电子邮件。这些是来自医院 ER 部门的 X 射线等订单,订单为“Stat”,电子邮件需要尽快发送给 X 射线技术人员。如果我把电子邮件放在我基本上等同于电子邮件队列的地方,从我今天读到的内容来看,我可能需要创建一个队列来触发这些。这是否是正确的方法,并且仍然是相当即时的?
  • 您无需 QUEUE 即可立即或快速启动这些功能。不确定您在做什么,但是如果您将逻辑保留在触发器中,它将是即时的。如果您正在创建一个永久表,那么您可以安排处理该表的代码尽可能频繁地运行。或者让它成为一个始终运行的服务,并且每 5 秒登录一次以查找新行。
猜你喜欢
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多