【问题标题】:trigger for insert or update after checking relationship检查关系后触发插入或更新
【发布时间】:2016-12-09 13:25:11
【问题描述】:

我有这 3 张桌子:

而且我需要建立一个触发器:约会(“encontro”)只有在 2 个个人资料(“perfis”)之间存在友谊(“amizade”)时才有效。

我已经创建了这个触发器,但我感到迷茫..帮助我

CREATE TRIGGER relaçoes_after_insert
ON encontros
INSTEAD OF insert       -
as
begin

    declare @idperfilA int;
    declare @idperfilB int;
    declare @data datetime;
    declare @count int;

    declare cursor_1 cursor for select * from inserted;
    open cursor_1;
    fetch next from cursor_1 into @idperfilA, @idperfilB, @data;

    WHILE @@FETCH_STATUS = 0
    BEGIN

        if exists( select * from inserted i, amizade a
               where i.IDPERFILA = a.IDPERFILA and i.IDPERFILB = a.IDPERFILB and GETDATE() > DATA)
        RAISERROR('there isnt friendship', 16, 10);
    else
        insert into ENCONTROS select * from inserted;
end;

        fetch next from cursor_1 into @idperfilA, @idperfilB, @data;

    END
    close cursor_1;
    deallocate cursor_1;

【问题讨论】:

  • 插入记录上的IDPERFILAIDPERFILB 值是否总是与AMIZADE 表中对应的A 或B 值匹配?或者你会遇到@idperfilA = AMIZADE.IDPERFILB@idperfilB = AMIZADE.IDPERFILA 的情况吗?
  • 我可能有点糊涂,但if exists ...where i.idperfila = a.idperfila and i.idperfilb = a.idperfilb and getdate() > data 不是说有友谊吗? -- 此外,您不需要光标。
  • @SqlZim 我假设游标是用来处理多行插入的(就像在this question 中一样) - 即使可能是这种情况,它仍然没有必要吗?
  • 应该是if not exists。 @3N1GM4,需要使用游标或临时表才能通过多行插入引发错误。为了避免游标,她可以将插入和 amizade 的左连接插入临时表,检查空值,如果有则引发错误,否则插入 encontros。我怀疑临时表方法可能会稍微快一些。
  • Raquel,您应该避免使用隐式连接(使用,),而应使用join 运算符来使用显式连接。

标签: sql-server triggers sql-update sql-insert


【解决方案1】:

我认为更好的答案是为此创建使用触发器。相反,我会在encontrosamizade 之间创建并强制执行外键约束

据我所知,这将导致做你想做的事,而无需编写自己的代码来尝试重新创建数据库提供的行为。从数据库设计的角度来看,它也更容易理解。

alter table dbo.encontros
  add constraint fk_amizade__encontros 
  foreign key (idperflia, idperflib) references dbo.amizade (idperflia, idperflib) 
  /* optional 
     on delete { no action | cascade | set null | set default } -- pick one, usual defualt is: no action
     on update { no action | cascade | set null | set default } -- pick one, usual defualt is: no action 
 --*/*
 ;

更多关于table constraints

无操作 SQL Server 数据库引擎引发错误,并且对父表中的行的删除操作被回滚。

级联 如果从父表中删除该行,则从引用表中删除相应的行。

设置为空 当删除父表中的相应行时,构成外键的所有值都设置为 NULL。要执行此约束,外键列必须可以为空。

设置默认值 当删除父表中的相应行时,构成外键的所有值都设置为其默认值。要执行此约束,所有外键列都必须具有默认定义。如果列可以为空并且没有设置显式默认值,则 NULL 将成为该列的隐式默认值。


根据您对@3N1GM4 的回复:

@3N1GM4 如果存在某个日期在今天之后的友谊(例如),这是一个错误,因此该友谊不存在。但我不知道在这一点上是否重要。 IDPERFILA 和 IDPERFILB 将匹配 amizade 表中的 A 和 B,但我需要确保它们不一样

您可以在amizade 上创建一个检查约束,以防止将具有无效日期的行插入到表中。

alter table dbo.amizade
  add constraint chk_data_lt_getdate ([data] < get_date());

更多关于check constraints;来自Gregory Larson 的更多示例。


原答案:

我仍在等待对该问题的澄清,但其中一个版本应该是正确的:

create trigger relaçoes_after_insert
on encontros
instead of insert
as
begin

/* To abort when any row doesn't have a matching friendship */
if not exists (
  select 1 
    from inserted i 
    where exists (
      select 1 
      from amizade a 
      where a.idperfila = i.idperfila 
        and a.idperfilb = i.idperfilb 
         and getdate() > data  /* not sure what this part does */
        /* as @3N1GM4 pointed out, 
         if the position doesn't matter between idperflia and idperflib then:
      where (i.idperfila = a.idperfila and i.idperfilb = a.idperfilb) 
         or (i.idperfila = a.idperfilb and i.idperfilb = a.idperfila)
        */
          )
  begin;
    raiserror('there isnt friendship', 16, 10);
    else 
    insert into encontros 
      select * from inserted;
  end;
end;

/* To insert all rows that have a matching friendship, you could use this instead */
  insert into encontros 
    select i.* 
      from inserted i
      where exists (
        select 1 
        from amizade a 
        where a.idperfila = i.idperfila 
          and a.idperfilb = i.idperfilb 
           and getdate() > data  /* not sure what this part does */
          /* as @3N1GM4 pointed out, 
           if the position doesn't matter between idperflia and idperflib then:
        where (i.idperfila = a.idperfila and i.idperfilb = a.idperfilb) 
           or (i.idperfila = a.idperfilb and i.idperfilb = a.idperfila)
          */
            )
end;

我看到的唯一潜在问题是使用inner join 而不是exists 作为第二个选项(插入有友谊的行并忽略没有友谊的行)是否可能存在@987654332 的问题@ 会从每个返回匹配项的条件中返回插入行的重复项。

【讨论】:

  • 非常感谢!但我不太明白,我需要在触发器之前插入一些东西,否则触发器会尝试同时插入正确和不正确的记录?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 2016-10-13
  • 1970-01-01
  • 2019-07-28
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
相关资源
最近更新 更多