检查关系后触发插入或更新

时间:2016-12-09 13:25:11

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

我有这3张桌子:

enter image description here

我需要构建一个触发器:日期(" encontro")只有在两个配置文件之间存在友谊(" amizade")时才有效(" perfis& #34;。)

我创造了这个触发器,但我感到迷茫..请帮帮我

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;

1 个答案:

答案 0 :(得分: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数据库引擎引发错误,并回滚父表中行的删除操作。

     

<强> CASCADE   如果从父表中删除该行,则会从引用表中删除相应的行。

     

SET NULL   当删除父表中的相应行时,组成外键的所有值都设置为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看到的唯一潜在问题(插入具有友谊的行并忽略那些不相关的行)是否有可能是(i.idperfila = a.idperfila and i.idperfilb = a.idperfilb) or (i.idperfila = a.idperfilb and i.idperfilb = a.idperfila)将从返回匹配的每个条件返回插入行的重复项的问题。