2016-02-02 81 views
1

我想创建修改现有触发器的过程。触发器负责阻止使用特定ID更新的行。我想类似的东西:修改现有触发器的步骤

CREATE PROCEDURE Change_trigger 
    @List_of_ids varchar(8000) 
AS 
ALTER TRIGGER blocks 
ON ttt 
INSTEAD OF update 
AS 
BEGIN 
    If (SELECT Id_ttt FROM inserted) IN (@List_of_ids) 
     BEGIN 
      raiserror('You cannot modify this record.', 12, 1) 
      RETURN 
     END 

    UPDATE ttt 
    SET 
    field1 = INSERTED.field1 
    FROM INSERTED 
    WHERE INSERTED.Id_ttt = ttt.Id_ttt 
END 

参数@List_of_ids会是这样:2,3,4,5,9,52。但是当我尝试创建此过程时出现错误:

Msg 156, Level 15, State 1, Procedure Change_trigger, Line 4
Incorrect syntax near the keyword 'TRIGGER'.

触发器已创建。

+1

而不是不断修改触发器,为什么不只是一个包含触发器然后引用的禁止ID的表?然后程序可以照顾填充这张表 - 如果这个程序是需要的话。顺便说一下,SQL Server内置了*设计*来处理多个值(表值参数,XML)。 'varchar'不是其中之一。 –

+1

此外,您的触发器已损坏。 “IN”运算符在其左侧需要一个标量值。当'inserted'包含*多行*(因为它很容易)时,触发器会产生一个错误。 (当然,单个更新可能会影响块列表中的某些*行,以及某些*行不是这样的行。您想如何处理这种情况?目前,所有这些更新都会被拒绝,但那是你想要的*还是仅仅是你已经建立了这个触发器的意外?) –

+0

@Damien_The_Unbeliever,谢谢你的评论。那么如何处理一个查询中的多行更新?当我想保持这样的触发器时,要阻止特定的ID?你还提到其他表格在那里存储禁止的ID。这真有趣。但我需要程序,因为这个阻塞ID将会改变 – XardasLord

回答

1

这是我会写,一旦触发。

ALTER TRIGGER blocks 
ON ttt 
INSTEAD OF update 
AS 
BEGIN 
    SET NOCOUNT ON 

    UPDATE t 
    SET 
    field1 = i.field1 
    FROM INSERTED i 
      inner join 
     ttt t 
      on i.Id_ttt = t.Id_ttt 
      left join 
     ttt_blocked on tb 
      on 
       i.Id_ttt = tb.Id_ttt 
    WHERE 
     tb.Id_ttt is null 
END 

注意,这触发不再抛出错误阻止更新,但它确实允许一个混合版本(某些行受阻,一些行不)发生。有没有干净的方式来提出错误,同时仍然部分应用触发器中的更新

然后我有一个表(如上文引用):

CREATE TABLE ttt_blocked (
    Id_ttt int not null, 
    constraint PK_ttt_blocked PRIMARY KEY (Id_ttt) 
) 

然后,如果有必要,我会创建一个过程来维护这个而不是不断变化的数据库模式:

CREATE PROCEDURE Change_blocking 
    @BlockedIDs xml 
AS 
    --Better option would be table-valued parameters 
    --but I've chosen to do XML today 

    --We expect the XML to be of the form 
    --<blocks> 
    -- <id>10</id> 
    -- <id>15</id> 
    --</blocks> 

    MERGE INTO ttt_blocked t 
    USING (select x.id.value('text()[1]','int') 
      from @BlockedIDs.nodes('/blocks/id') x(id)) s(Id_ttt) 
    ON 
     t.Id_ttt = s.Id_ttt 
    WHEN NOT MATCHED THEN INSERT (Id_ttt) VALUES (s.Id_ttt) 
    WHEN NOT MATCHED BY SOURCE THEN DELETE; 

正如我也影射上面,我通常建议Table-Valued Parameters而不是XML(和它们中的领先varchar因为他们是设计来保存多个值),但它会增加更多的代码给这个答案。

+0

而不是创建过程,我可以直接从我的应用程序权限添加/删除行ttt_blocked表?我不需要程序 – XardasLord

+1

@ XardasLord - 是的,这就是为什么我在回答“必要时”或在评论中“如果程序是需要的”。 –

+0

我有一个问题,是否有任何方式来保持这样的条件:'如果(SELECT Id_ttt FROM插入)IN(SELECT Id_ttt FROM ttt_blocked)'并显示记录被阻止时的错误消息?但我的意思是一个更新几行的查询。通过“插入”或某些东西循环? – XardasLord

1

试试这个..

CREATE PROCEDURE Change_trigger 
    @List_of_ids varchar(4000) 
AS 
begin 
declare @sql varchar(8000) 

set @sql =' 
ALTER TRIGGER blocks 
ON ttt 
INSTEAD OF update 
AS 
BEGIN 
    if exists (SELECT Id_ttt FROM inserted where Id_ttt IN ('[email protected]_of_ids+')) 
     BEGIN 
      raiserror(''You cannot modify this record.'', 12, 1) 
      RETURN 
     END 

    UPDATE ttt 
    SET 
    field1 = INSERTED.field1 
    FROM INSERTED 
    WHERE INSERTED.Id_ttt = ttt.Id_ttt 
END' ; 
exec (@sql); 
END 
+0

很好,程序已创建。但是当我尝试执行它时,我得到了这个错误:'Msg 102,Level 15,State 1,Procedure change,Line 40 对象“Id_ttt”'附近的语法错误。我用这个来执行:'EXEC Change_trigger @List_of_ids ='2,4,6'' – XardasLord

+0

SELECT Id_ttt FROM insert where Id_ttt IN(@List_of_ids)。这是正确的systax吗? –

+1

你不能只是'IF(查询)'。你可能在寻找'EXISTS'。此外,Xardas报告的错误可能是通过为触发器的BEGIN添加匹配的“END”来解决的。 –