2016-06-10 15 views
0

请检查我的代码,我很少用触发器与AFTER子句,非常感谢:触发器在Oracle中:没有找到数据

set serveroutput on; 
create or replace trigger tvideo_2 after insert on video for each row 
declare 
pragma autonomous_transaction; 
v_title video.title%type; /* Declare a variable to check title that contain '18+' */ 
begin 
select title into v_title from video where video.videoid=:new.videoid; 
if v_title like '%18+%' then 
update video 
set age=18 
where videoid=:new.videoid; 
dbms_output.put_line('Video '||:new.videoid||' has been updated age to 18+'); 
else 
dbms_output.put_line('Video '||:new.videoid||' is not 18+!'); 
end if; 
end; 
/
insert into video values('V5', '18+', 240, 19); 
VIDEO properties: (videoid, title, duration, age) 
+1

这不是触发器的最佳用法。您正在使用自主事务来解决突变触发器错误,但这增加了其他问题的可能性。当从触发器调用DBMS_output时不会显示任何内容。为什么不使用插入前设置年龄? – kevinsky

+2

我猜你想要使用插入或更新触发器之前,你想要使用':new.title'而不是从'video'中选择,并且你想要修改':new.title'而不是在'video'上做'更新'。然而,当你有错误时,包含错误而不是希望我们能猜出错误是什么(并且确切地说你想要什么行为)会更有意义。用合理的缩进格式化你的代码也是很好的。 –

+0

如果我创建一个包含'V5'的数据,它会工作正常(我必须删除videoido是主键,没有任何限制)。我经常在before子句中使用trigger来检查插入的行并调用raise_app_error。但是在这种情况下,您必须插入一行,然后触发器会检查标题并更新标题是否包含18+,否则不打印“不是18+” - 这意味着触发后(Oracle课程的要求:练习考试。已经完成了它,它不能运行...)。对不起,我的英语不好。 xD – mmo2112

回答

0

使用pragma autonomous_transaction意味着触发无法看到刚刚插入的行和导致它被解雇。

当你删除主键并且预先插入了一个具有相同ID的记录时,它的行将被你的触发器更新,而不是你插入的新行 - 所以它仍然不会做什么你想要的,重复PK值是不明智的。

如果必须将其作为after insert触发器执行此操作,则可以删除for each row,这样可以避免大概试图避免的突变表问题,但是必须查询整个表才能找到要更新的行:

create or replace trigger tvideo_2 after insert on video 
begin 
    update video 
    set age = 18 
    where title like '%18+%' 
    and age != 18; 
end; 
/

或者,如果你想打印一个消息:

create or replace trigger tvideo_2 after insert on video 
begin 
    for rec in (
    select videoid, title 
    from video 
    where title like '%18+%' 
    and age != 18 
) 
    loop 
    update video 
    set age = 18 
    where video.videoid = rec.videoid; 
    dbms_output.put_line('Video '||rec.videoid||' has been updated age to 18+'); 
    end loop; 
end; 
/

但在任何存储的PL/SQL中使用触发器dbms_output,还是真的,是不是一个好主意 - 如果客户端会话那插入没有'不看消息的输出缓冲区,而且很可能没有任何东西可以看到它。

你也可以使用明确的游标for update然后update .. where current of

无论采用哪种方式,您都必须查询整个表格,这并不是非常高效 - 每次插入新表格时都会检查所有现有行,而不是仅仅检查单个新值。一个触发器并不适合做那么多工作 - 即使它只是实际更新一行(或没有!),它必须每插入查询所有内容()。这不会很好地扩展。

还有其他方法可以避免突变表问题,但任何事情都将成为黑客攻击。明智的方式做,这是一个之前,插入行级触发器:

create or replace trigger tvideo_2 before insert on video for each row 
begin 
    if :new.title like '%18+%' then 
    :new.age := 18; 
    dbms_output.put_line('Video '||:new.videoid||' has been updated age to 18+'); 
    else 
    dbms_output.put_line('Video '||:new.videoid||' is not 18+!'); 
    end if; 
end; 
/

(约使用dbms_output相同的警告),但是,这似乎是禁止的某些原因。

+0

哇,对我来说太棒了!我喜欢选项#2和#3。但老师强调AFTER子句,所以2更好。我从Oracle读取,他们解释说: AFTER表示首先执行语句,然后激活触发器。 BEFORE表示首先触发触发器然后激活语句(我经常使用它,但老师已经操我了......)。 你能解释一下关于AFTER的更多差异吗(如果没有'每行'意味着不能使用:new和:old,必须使用cursor或select语句来获取像opt#2这样的变量)和BEFORE?也许这会更好,如果你有一些例子,非常感谢兄弟! : - * – mmo2112

+1

这里最好问(好)问题,这样可以分享知识。另外还有很多其他人比我知道得多。我学的比我教的多... –

+0

好,我明白了!你能否解释更多关于'AFTER'和'BEFORE'触发器之间的区别?在使用'BEFORE'触发器时,我经常会向变量发送一个数据。用'AFTER'子句='BEFORE for each row'代替开头的代码。为什么选择语句'将视频中的标题从视频中选择为v_title其中videoid =:new.videoid;'虽然没有数据:'new.videoid ='V5''? – mmo2112