Oracle数据库中有关触发器问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle数据库中有关触发器问题相关的知识,希望对你有一定的参考价值。

参考技术A

  触发器是一种特殊类型的存储过程 它不同于存储过程 触发器主要是通过事件进行触发而被执行的 触发器的触发事件分可为 类 分别是DML事件 DDL事件和数据库事件 而存储过程可以通过存储过程名字而被直接调用 当对某一表进行诸如UPDATE INSERT DELETE 这些操作时 SQL Server 就会自动执行触发器所定义的SQL 语句 从而确保对数据的处理必须符合由这些SQL 语句所定义的规则

  触发器是特定事件出现的时候 自动执行的代码块 类似于存储过程 但是用户不能直接调用他们

   触发器功能

   允许/限制对表的修改

   自动生成派生列 比如自增字段

   强制数据一致性

   提供审计和日志记录

   防止无效的事务处理

   启用复杂的业务逻辑

  触发器种类

  触发器的种类可划分为 种 数据操纵语言(DML)触发器 替代(INSTEAD OF)触发器 数据定义语言(DDL)触发器 数据库事件触发器

  数据操纵语言(DML)触发器 简称DML触发器 是定义在表上的触发器 创建在表上 由DML事件引发的触发器 编写DML触发器时的两点要素是 确定触发的表 即在其上定义触发器的表 确定触发的事件 DML触发器的触发事件有INSERT UPDATE和DELETE三种;替代触发器 简称INSTEAD OF触发器 创建在视图上 用来替换对视图进行的删除 插入和修改操作; 数据定义语言(DDL)触发器 简称DDL触发器 定义在模式上 触发事件是数据对象的创建和修改;数据库事件触发器 定义在整个数据库或模式上 触发事件是数据库事件

  ORACLE产生数据库触发器的语法为

  CREATE [OR REPLACE] TRIGGER 触发器名

  BEFORE|AFTER|INSTEAD OF 触发事件 [OR 触发事件 ]

  ON 表名

  WHEN 触发条件

  [FOR EACH ROW]

  DECLARE

  声明部分

  BEGIN

  主体部分

  END;

  其中

  触发器名 触发器对象的名称 由于触发器是数据库自动执行的 因此该名称只是一个名称 没有实质的用途 一个触发器可由多个不同的数据操纵语言操作触发 在触发器中 可用INSERTING DELETING UPDATING谓词来区别不同的数据操纵语言操作 这些谓词可以在IF分支条件语句中作为判断条件来使用

  触发时间 指明触发器何时执行 该值可取 触发的时间有BEFORE和AFTER两种 分别表示触发动作发生在DML语句执行之前和语句执行之后 确定触发级别 有语句级触发器和行级触发器两种 语句级触发器表示SQL语句只触发一次触发器 行级触发器表示SQL语句影响的每一行都要触发一次

  Before 表示在数据库动作之前触发器执行;在SQL语句的执行过程中 如果存在行级BEFORE触发器 则SQL语句在对每一行操作之前 都要先执行一次行级BEFORE触发器 然后才对行进行操作 如果存在行级AFTER触发器 则SQL语句在对每一行操作之后 都要再执行一次行级AFTER触发器

  after 表示在数据库动作之后出发器执行 如果存在语句级AFTER触发器 则在SQL语句执行完毕后 要最后执行一次语句级AFTER触发器

  触发事件 指明哪些数据库动作会触发此触发器 指INSERT DELETE或UPDATE事件 事件可以并行出现 中间用OR连接;

  insert 数据库插入会触发此触发器;

  update 数据库修改会触发此触发器;

  delete 数据库删除会触发此触发器

  表 名 数据库触发器所在的表

  for each row 表示触发器为行级触发器 省略则为语句级触发器 对表的每一行触发器执行一次

  触发器的创建者或具有DROP ANY TIRGGER系统权限的人才能删除触发器 删除触发器的语法如下

  DROP TIRGGER 触发器名

  可以通过命令设置触发器的可用状态 使其暂时关闭或重新打开 即当触发器暂时不用时 可以将其置成无效状态 在使用时重新打开 该命令语法如下

  ALTER TRIGGER 触发器名 DISABLE|ENABLE

  其中 DISABLE表示使触发器失效 ENABLE表示使触发器生效

  同存储过程类似 触发器可以用SHOW ERRORS 检查编译错误

  如果有多个触发器被定义成为相同时间 相同事件触发 且最后定义的触发器是有效的 则最后定义的触发器被触发 其他触发器不执行 触发器体内禁止使用MIT ROLLBACK SAVEPOINT语句 也禁止直接或间接地调用含有上述语句的存储过程 定义一个触发器时要考虑上述多种情况 并根据具体的需要来决定触发器的种类

   触发器的作用

  触发器的主要作用就是其能够实现由主键和外键所不能保证的复杂的参照完整性和数据的一致性 除此之外 触发器还有其它许多不同的功能

  ( ) 强化约束(Enforce restriction)

  触发器能够实现比CHECK 语句更为复杂的约束

  ( ) 跟踪变化Auditing changes

  触发器可以侦测数据库内的操作 从而不允许数据库中未经许可的指定更新和变化

  ( ) 级联运行(Cascaded operation)

  触发器可以侦测数据库内的操作 并自动地级联影响整个数据库的各项内容 例如 某个表上的触发器中包含有对另外一个表的数据操作(如删除 更新 插入)而该操作又导致该表上触发器被触发

  ( ) 存储过程的调用(Stored procedure invocation)

  为了响应数据库更新触 发器可以调用一个或多个存储过程 甚至可以通过外部过程的调用而在DBMS( 数据库管理系统)本身之外进行操作

  由此可见 触发器可以解决高级形式的业务规则或复杂行为限制以及实现定制记录等一些方面的问题 例如 触发器能够找出某一表在数据修改前后状态发生的差异 并根据这种差异执行一定的处理 此外一个表的同一类型(INSERT UPDATE DELETE)的多个触发器能够对同一种数据操作采取多种不同的处理

   总体而言 触发器性能通常比较低

lishixinzhi/Article/program/Oracle/201311/18340

有关触发器(ORA-39083,ORA-00942)的Oracle IMPDP REMAP_SCHEMA问题是否有一个很好的解决方法?

可以使用Oracle数据泵导入工具(IMPDP.EXE)使用REMAP_SCHEMA选项将一个模式导入另一个模式。但是,存在一个问题,即触发器未正确重新映射。这导致触发器根本没有创建错误,如下所示:

ORA-39083: Object type TRIGGER failed to create with error: ORA-00942: table or view does not exist Failing sql is: CREATE TRIGGER "**NEW_SCHEMA**"."METER_ALARMS_BI"   BEFORE INSERT ON
**OLD_SCHEMA**.METER_ALARMS ...

原因是因为create SQL仍然引用OLD_SCHEMA。它确实在Oracle文档中说:

映射可能不是100%完成,因为某些模式引用导入无法查找。例如,Import将找不到嵌入在类型,视图,过程和包的定义主体中的模式引用。

恕我直言,这是甲骨文的一个警察,但这是另一个讨论!

根据Oracle Metalink说明750783.1,解决方法是:

  1. 创建SQL FILE以包含相关的DDL命令):
      impdp system/****** directory=test_dp
      DUMPFILE=export_schemas.dmp
     remap_schema=u1:u2 sqlfile=script.sql
  1. 从写入的SQLFILE中提取受影响的DDL并更正模式引用。然后手动执行该命令。

这不是一个好方法,特别是如果你有许多失败的对象,并希望自动化组合多个模式的过程,以便在数据库的字段升级。

有没有人找到更好的方法来做到这一点?如果要在现场使用,我需要一个必须100%可靠的解决方案。我可以解析生成的SQL文件,但可以100%正确吗?是否有一些方法可以拦截IMPDP执行的CREATE SQL语句并在导入时动态更正?可以直接修补DMP文件吗?

答案

你可以看一下DBMS_METADATA

有一个REMAP_SCHEMA选项。不确定它是否会比DATAPUMP更好(我怀疑DATAPUMP将使用DBMS_METADATA)。但是“后处理”输出会更容易。

另一答案

我认为这取决于模式名称是否可以作为非模式名称的一部分出现在您的代码中。例如,您是否具有包含与模式名称相同的字符的变量名称。如果没有,那么我认为编写生成的触发器创建脚本替换旧模式的进程的脚本编写起来并不困难。也许你可以使用datapump导出/导入没有文本代码的对象类型(不是触发器,包,程序,函数等),然后为代码对象转储SQL,只需用新的替换旧模式。

如果旧模式名称出现在您不想替换的位置,则会更难。您可以提取代码对象并尝试创建它们并收集所有错误。然后获取失败的对象的名称,并尝试使用newschema.objectname根据错误替换oldschema.objectname并重新运行。

假设没有像oldschema这样的字符串,您可以在触发器文本中编辑模式的示例。你不想要替换:

Example

SQL> 
SQL> set define off
SQL> 
SQL> drop table test1.tab1;

Table dropped.

SQL> drop table test1.tab2;

Table dropped.

SQL> 
SQL> create table test1.tab1
  2  (
  3     col1 number,
  4     col2 number
  5  );

Table created.

SQL> 
SQL> create table test1.tab2
  2  (
  3     col1 number,
  4     col2 number
  5  );

Table created.

SQL> 
SQL> create or replace trigger test1.trg1
  2  before insert or update on test1.tab1
  3  for each row
  4  begin
  5    :new.col2 := :new.col1*2;
  6  end;
  7  /

Trigger created.

SQL> 
SQL> create or replace trigger test1.trg2
  2  before insert or update on test1.tab2
  3  for each row
  4  begin
  5    :new.col2 := :new.col1*2;
  6  end;
  7  /

Trigger created.

SQL> 
SQL> drop table clobout;

Table dropped.

SQL> 
SQL> create table clobout (doc clob);

Table created.

SQL> 
SQL> declare
  2  h NUMBER; --handle returned by OPEN
  3  th NUMBER; -- handle returned by ADD_TRANSFORM
  4  doc CLOB;
  5  BEGIN
  6  
  7  -- Specify the object type.
  8  h := DBMS_METADATA.OPEN('TRIGGER');
  9  
 10  -- Use filters to specify the particular object desired.
 11  DBMS_METADATA.SET_FILTER(h,'SCHEMA','TEST1');
 12  
 13  -- Request that the schema name be modified.
 14  th := DBMS_METADATA.ADD_TRANSFORM(h,'MODIFY');
 15  DBMS_METADATA.SET_REMAP_PARAM(th,'REMAP_SCHEMA','TEST1','TEST2');
 16  
 17  -- Request that the metadata be transformed into creation DDL.
 18  th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL');
 19  
 20  dbms_metadata.set_transform_param(th,'SQLTERMINATOR',true);
 21  
 22  -- Fetch the triggers.
 23  
 24  LOOP
 25    doc := DBMS_METADATA.FETCH_CLOB(h);
 26    EXIT WHEN (doc is null);
 27    insert into clobout values (doc);
 28    commit;
 29  END LOOP;
 30  
 31  -- Release resources.
 32  DBMS_METADATA.CLOSE(h);
 33  END;
 34  /

PL/SQL procedure successfully completed.

SQL> 
SQL> -- update schema name in triggers
SQL> 
SQL> update clobout set doc=replace(doc,'test1.','test2.');

2 rows updated.

SQL> 
SQL> commit;

Commit complete.

SQL> 
SQL> select doc from clobout;

  CREATE OR REPLACE EDITIONABLE TRIGGER "TEST2"."TRG1"
before insert or update on test2.tab1
for each row
begin
  :new.col2 := :new.col1*2;
end;
/
ALTER TRIGGER "TEST2"."TRG1" ENABLE;


  CREATE OR REPLACE EDITIONABLE TRIGGER "TEST2"."TRG2"
before insert or update on test2.tab2
for each row
begin
  :new.col2 := :new.col1*2;
end;
/
ALTER TRIGGER "TEST2"."TRG2" ENABLE;


SQL> 
SQL> spool off

以上是关于Oracle数据库中有关触发器问题的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 中的审计

有关触发器(ORA-39083,ORA-00942)的Oracle IMPDP REMAP_SCHEMA问题是否有一个很好的解决方法?

通过 JDBC (oracle) 创建触发器

ORACLE数据库实现自增的两种方式

oracle数据库表实现主键自增功能

oracle 数据库的触发器