我有一个包含一些数据的单独表格,我希望每次在该表格中插入新行时创建一个新角色。
我创建了以下触发器:
create or replace
TRIGGER TestTrigger
BEFORE INSERT ON TestTable
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW BEGIN
create role :New.RoleName;
END;
但触发器无法编译。 我有错误:PLS-00103:PLS-00103:当遇到以下情况之一时遇到符号“CREATE”:(如果循环mod为空pragma提升返回选择更新,则使用<<继续关闭,开始情况声明退出goto current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
这可能吗?
答案 0 :(得分:4)
我们不能以任何形式的PL / SQL本地执行DDL。包括触发器。为此,我们需要使用动态SQL。
触发器有一个额外的皱纹:它们作为事务的一部分被触发,并且它们有一个限制,禁止我们在其体内发出提交。在Oracle中,任何DDL命令都会发出两个提交,一个在执行DDL语句之前,一个在执行之后。因此,要在触发器中执行DDL,我们必须使用autonomous_transaction pragma
,这意味着DDL在单独的嵌套事务中运行。
create or replace TRIGGER TestTrigger
BEFORE INSERT ON TestTable
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
declare
pragma autonomous_transaction;
BEGIN
execute immediate 'create role '|| :New.RoleName;
END;
自主交易是我们容易滥用和破坏我们自己的应用程序的结构之一。在你的场景中,障碍是CREATE ROLE可以在其事务泡沫中成功,而INSERTT进入TestTable
失败;这就是“自主交易”的含义。所以你仍然不能保证“[你的]表和oracle角色之间的一致性”。
更好的解决方案是将两个语句都包含在一个程序调用中,而不是试图欺骗DML做一些它不应该做的事情。
create or replace procedure create_role
( p_role_name in user_roles.role%type
, p_desc in testtable.description%type )
is
pragma autonomous_transaction;
begin
insert into testtable
( id, creationdate, rolename, description)
values
( some_seq.nextval, sysdate, p_role_name, p_desc );
execute immediate 'create role '|| p_role_name;
end;