这样说吧,你的应用涉及到间接递归触发器,那就需要在触发器代码中防止无限递归,有几个方法可以防止无限递归
1用update()函数判断修改的字段,这只能避免一部分
2查看修改前后的值,进行比较,真正的值作了修改才递交update

解决方案 »

  1.   

    你的意思没有表达清楚,是只updateA表时触发器可以更新一次还是无数次,还是只针对于数据如果他被Upate过就不能再update了?后者你可以使用一个MARK字段即可。
      

  2.   

    nested triggers 选项是全局的, 当然影响所有的触发器.
      

  3.   

    如果只想某个或者某些触发器限制此行为, 应该在触发器代码中做控制, 一般有如下几种方法:
    1. 使用 update(列名) 函数
       对于"A表update后,取B表某列再次UPDATE A表"
       如果仅更新A表的某些列才触发 UPDATE B, 并且 B 表再次 UPDATE A表不会包含A表触发UPDATE B的那些列, 则在A表的触发器中, 使用 IF UPDATE(列) 来确定是否应该UPDATE B即可
    2. 使用 @@NESTLEVEL
       该变量值确定嵌套层数
       对于"A表update后,取B表某列再次UPDATE A表"
       如果触发者不是一个存储过程, 则 UPDATE A 的A表触发器 @@NESTLEVEL = 1, 到UPDATE B时, B表触发器  @@NESTLEVEL = 2, B表触发器再 UPDATE A时,  @@NESTLEVEL = 3
       所以如果  @@NESTLEVEL >=3 时, 一般表示递归了(当然, 前提是 UPDATE A的触发器本身没有两层的递归, 即不能是存储过程再调用存储过程去 UPDATE A
      

  4.   

    只能用条件判断不递交UPDATE吗?
      

  5.   

    如果前面两种方法都不能解决问题, 还有方法3
    使用  @@PROCID
    该全局变量返回调用者的 object_id , 在A表触发器中使用它就可以知道触发者是谁, 如果是B表触发器, 则不处理就行了, 类似下面这样:IF OBJECT_ID(N'B表触发器名称') = @@PROCID
    BEGIN
        PRINT 'B表触发器, 不处理'
        RETURN
    END
      

  6.   

    只能用条件判断不递交UPDATE吗?
    -------------------------------------这是什么意思? 你是说不在 B 中递交 UPDATE A么? 如果是的话, 岂不是与你的需求冲突? 你的需求是要 UPDATE A 的嘛, 这个当然会触发A表触发器, 那也当然需要在A表触发器判断了
      

  7.   

    其实我本身的要求是:表A在INSERT时触发写入表B,再把表B中的ID再写入表A,但表A中又不想让别人更新该列。如果更新则重新从表B中取回ID.
      

  8.   

    举个已经测试的例子,这是个保持两个表内容统一的触发器例子,本来可以设置nested triggers 选项简单解决,现在是在递归触发器打开的情况下,通过触发器代码来解决.
    --建表
    create table T1(
    id int not null PRIMARY KEY,
    value int
    )
    gocreate table T2(
    id int not null PRIMARY KEY,
    value int
    )
    go--触发器代码
    Create Trigger tr_T1_Insert
    on T1
    for Insert
    as
    set nocount on
    if exists (                                    --这个判断是防止无限递归的
    select * from inserted i
    where not exists (
    select 1 from T2 where id=i.id
    )
    )
    insert T2
    select * from inserted i
    where not exists (
    select 1 from T2 where id=i.id
    )print '1I'                        --报告触发器
    goCreate Trigger tr_T2_Insert
    on T2
    for Insert
    as
    set nocount on
    if exists (                                 --这个判断是防止无限递归的
    select * from inserted i
    where not exists (
    select 1 from T1 where id=i.id
    )
    )
    insert T1
    select * from inserted i
    where not exists (
    select 1 from T1 where id=i.id
    )
    print '2I'                        --报告触发器
    goCreate Trigger tr_T1_Update
    on T1
    for update
    as
    set nocount on
    if update (value)
    begin
       if exists (                                       --这个判断是防止无限递归的
       select 1
       from T2,Inserted i,deleted d
       where i.id=d.id
       and i.id=T2.id
       and i.value<>d.value
       )
       update T2
          set value=i.Value
       from T2,Inserted i,deleted d
       where i.id=d.id
       and i.id=T2.id
       and i.value<>d.value
    endprint '1U'                        --报告触发器
    goCreate Trigger tr_T2_Update
    on T2
    for update
    as
    set nocount on
    if update (value)
    begin
       if exists (                                           --这个判断是防止无限递归的
       select 1
       from T1,Inserted i,deleted d
       where i.id=d.id
       and i.id=T1.id
       and i.value<>d.value
       )
       update T1
          set value=i.Value
       from T1,Inserted i,deleted d
       where i.id=d.id
       and i.id=T1.id
       and i.value<>d.value
    endprint '2U'                        --报告触发器
    go
    Create Trigger tr_T1_delete
    on T1
    for delete
    as
    set nocount on
    if exists (                                               --这个判断是防止无限递归的
    select 1 from T2,deleted d
    where T2.id=d.Id
    )
    delete T2
    from deleted d
    where T2.id=d.Id
    print '1D'                        --报告触发器
    go
    Create Trigger tr_T2_delete
    on T2
    for delete
    as
    set nocount on
    if exists (                                                    --这个判断是防止无限递归的
    select 1 from T1,deleted d
    where T1.id=d.Id
    )
    delete T1
    from deleted d
    where T1.id=d.Id
    print '2D'                        --报告触发器
    go--设置递归触发器
    alter database ygback set RECURSIVE_TRIGGERS ON 
    go--测试1
    insert T1 values(1,1)--结果1
    2I
    1I
    select * from T2
    id          value       
    ----------- ----------- 
    1           1--测试2
    insert T1 select 2,2 union all select 3,3
    --结果2
    2I
    1I
    select * from T2
    id          value       
    ----------- ----------- 
    1           1
    2           2
    3           3--测试3
    update T1 
    set value=5
    where id=1--结果3
    1U
    2U
    1U
    select * from T2
    id          value       
    ----------- ----------- 
    1           5
    2           2
    3           3--测试4
    update T1 
    set value=value+1
    where id>1
    --结果4
    1U
    2U
    1U
    select * from T2
    id          value       
    ----------- ----------- 
    1           5
    2           3
    3           4
    --测试5
    delete T1 
    where id=1
    --结果4
    2D
    1D
    select * from T2
    id          value       
    ----------- ----------- 
    2           3
    3           4
      

  9.   

    其实我本身的要求是:表A在INSERT时触发写入表B,再把表B中的ID再写入表A,但表A中又不想让别人更新该列。如果更新则重新从表B中取回ID.-------------------------------------------既然是这样, 那么A表的INSERT触发器是不用理的, 因为B表做的是UPDATE
    故只需要在A表的UPDATE触发器判断是否UPDATE(ID)即可嘛, 类似:IF UPDATE(ID)
    BEGIN
        .....如果更新则重新从表B中取回ID
    END
    ELSE
    BEGIN
       ... 做正常的处理
    END
      

  10.   

    表A:
    HeadKey int
    SerialNO VARCHAR(15)
    表B:
    ID INT (自动增长)
    HeadKey int
    要求:
    A表插入时,同时插入B表,A表SerialNO将得到B表中ID的格式化字符。如果此时A表中SerialNO 被用户修改,则重新从表B中找回。
      

  11.   

    按楼主的要求, 根本B表不用触发器
    CREATE TRIGGER TR_A ON 表A
    FOR INSERT
    AS
    INSERT 表B SELECT HeadKey FROM inserted
    UPDATE A SET
    SerialNO = B.ID
    FROM 表A A, 表B B, inserted I
    WHERE A.HeadKey = B.HeadKey
    AND A.HeadKey = I.HeadKey
    GOCREATE TRIGGER TR_A ON 表A
    FOR UPDATE
    AS
    IF UPDATE(SerialNO)
    UPDATE A SET
    SerialNO = B.ID
    FROM 表A A, 表B B, inserted I
    WHERE A.HeadKey = B.HeadKey
    AND A.HeadKey = I.HeadKey
    GO
      

  12.   

    我认为 Create Trigger tr_T2_Update
    on T2
    for update
    as
    set nocount on
    if update (value)
    begin
       if exists (                                           --这个判断是防止无限递归的
       select 1
       from T1,Inserted i,deleted d
       where i.id=d.id
       and i.id=T1.id
       and i.value<>d.value
       )
       update T1
          set value=i.Value
       from T1,Inserted i,deleted d
       where i.id=d.id
       and i.id=T1.id
       and i.value<>d.value
    end 可以解决的
      

  13.   

    CREATE TRIGGER TR_A ON 表A
    FOR UPDATE
    AS
    IF UPDATE(SerialNO)
    UPDATE A SET
    SerialNO = B.ID
    FROM 表A A, 表B B, inserted I
    WHERE A.HeadKey = B.HeadKey
    AND A.HeadKey = I.HeadKey
    GO
    这样不是无限递归了吗?
      

  14.   

    另外:
    如果此时A表中SerialNO 被用户修改,则重新从表B中找回
    ---------------------------------------------------既然是这样, 感觉还不如如果有要胆敢更新 SerialNO, 则直接不让他的 UPDATE 成功(既然是违反政策方针政策, 姑息养奸没必要嘛)CREATE TRIGGER TR_A ON 表A
    FOR UPDATE
    AS
    IF UPDATE(SerialNO)
    BEGIN
        RAISERROR(N'禁止更新 SerialNO, 你的操作被取消', 1)
        ROLLBACK TRAN
    END
    GO
      

  15.   

    这样不是无限递归了吗?
    -----------------------------楼主提到的这个叫直接递归, 这个是数据库选项 RECURSIVE_TRIGGERS 控制的, 默认是关闭的, 难道楼主有将其打开么?
      

  16.   

    按照你的说法:UPDATE A SET
    SerialNO = B.ID
    FROM 表A A, 表B B, inserted I
    WHERE A.HeadKey = B.HeadKey
    AND A.HeadKey = I.HeadKey
    不会触发表A的UPDATE吗?
      

  17.   

    如果楼主确实把数据库的直接递归选项打开, 那么就参考我前面提到的防止递归的方法3处理就行了嘛
    CREATE TRIGGER TR_A ON 表A
    FOR UPDATE
    AS
    IF UPDATE(SerialNO) AND @@PROCID <> OBJECT_ID(N'TR_A')
    BEGIN
        RAISERROR(N'禁止更新 SerialNO, 你的操作被取消', 1)
        ROLLBACK TRAN
    END
    GO
      

  18.   

    A表触发器中更新A, 这种引发的叫直接递归, 是否允许是数据库的 RECURSIVE_TRIGGERS 选项控制, 默认是关闭的
    A表触发器更新B, 表表触发器再更新A, 这种引发的叫间接递归, 是否允许由服务器的nested triggers 选项控制, 默认是打开的(即允许的)
      

  19.   

    RECURSIVE_TRIGGERS 我这里的SQL SERVER都是打开的
      

  20.   

    wangzhenzhu1975(才疏学浅) ( ) 信誉:97  2007-07-30 14:56:24  得分: 0  
     
     
       表A:
    HeadKey int
    SerialNO VARCHAR(15)
    表B:
    ID INT (自动增长)
    HeadKey int
    要求:
    A表插入时,同时插入B表,A表SerialNO将得到B表中ID的格式化字符。
    如果此时A表中SerialNO 被用户修改,则重新从表B中找回。
      
    ---------------------------------------------------------------------
    如下触发器,应该不会递归create trigger tr_A_Insert
    on A
    for insert
    as
    set nocount on
    insert B (HeadKey) select HeadKey from insertedgo
    create trigger tr_B_Insert
    on B
    for insert
    as
    set nocount on
    if exists (
    select 1 from A, inserted i
    where A.HeadKey=i.HeadKey
    and A.SerialNO<>格式化(i.ID)
    )
    update A
    set SerialNO<>格式化(i.ID)
    from A, inserted i
    where A.HeadKey=i.HeadKey
    and A.SerialNO<>格式化(i.ID)go