一个触发器需求的案例

有一位兄弟,问了一问题,

用触发器实现一个功能,如果插入的字段AGE为空,则将此字段AGE的值置为0。

以下是一版实现,

SQL> create table t (id number, age number);
Table created.

SQL> CREATE OR REPLACE TRIGGER TR1
  AFTER INSERT ON T
  FOR EACH ROW
  WHEN (NEW.AGE='')
BEGIN
  UPDATE T SET AGE=0 WHERE ID = :NEW.ID;
END;
/

Trigger created.

执行插入操作,但NULL值,并未改为0,

SQL> insert into t values(1, '');
1 row created.

SQL> select * from t;
    ID      AGE
---------- ----------
     1

我对触发器,了解非常有限,只能试着来,乍一看判断空,即NULL,是不能用“=”,需要使用IS NULL/IS NOT NULL,改了一下,执行报错,

SQL> CREATE OR REPLACE TRIGGER TR1
  AFTER INSERT ON T
  FOR EACH ROW
  WHEN (NEW.AGE IS NULL)
BEGIN
  UPDATE T SET AGE=0 WHERE ID = :NEW.ID;
END;
/

Trigger created.

SQL> insert into t values(1, '');
insert into t values(1, '')
            *
ERROR at line 1:
ORA-04091: table BISAL.T is mutating, trigger/function may not see it
ORA-06512: at "BISAL.TR1", line 2
ORA-04088: error during execution of trigger 'BISAL.TR1'

oerr ora 4091
04091, 00000, "table %s.%s is mutating, trigger/function may not see it"
// *Cause: A trigger (or a user defined plsql function that is referenced in
//         this statement) attempted to look at (or modify) a table that was
//         in the middle of being modified by the statement which fired it.
// *Action: Rewrite the trigger (or function) so it does not read that table.

《After Update Trigger Fails With ORA-04091 When Modifying a Column in the Same Table (文档 ID 156388.1)》指出,

A mutating table is a table that is currently being modified by an update,
delete, or insert statement.  Oracle returns the ORA-04091 error if a row
trigger  reads or modifies the mutating table.  

For example, ifa trigger contains a select statement or an update statement referencing the table it is triggering off of.

翻译一下,mutating table是指一个当前正在被update,delete,insert语句修改的表,如果在一个行级别的trigger中读取或修改一个mutating table,则往往会遇到ORA-04091错误。例如,如果在trigger中使用了select或者update语句访问trigger所在的表,就像上面这个触发器。

解决方法,使用PLSQL存储需要更新行的ROWID,在触发器中使用这个值,即利用临时变量,保存行信息,

One way to handle this situation is to use a package PL/SQL table to store
ROWIDs of updated records in a row trigger, and reprocess the updated records
in a statement trigger. 

参考这篇文章,《SQL: Example Workaround for ORA-4091 Error (文档 ID 37861.1)》。

除此之外,自治事务是另一种方法,重新写触发器,插入数据后对刚插入这条无效,但对已有符合条件的数据有效,需求是能更新正insert是最好的,但是目前的逻辑就是insert一条null值,用触发器相当于收尾,更新所有已有的null记录,如下所示,

SQL> select * from t;
    ID      AGE
---------- ----------
     1

SQL> CREATE OR REPLACE TRIGGER TR1
  AFTER INSERT ON T
  FOR EACH ROW
  DECLARE
  t_rec NUMBER;
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  t_rec := 0;
  CASE
  when INSERTING then
  UPDATE T SET AGE=t_rec WHERE age is null;
  end case;
  COMMIT;
EXCEPTION when OTHERS THEN ROLLBACK;
END;
/

Trigger created.

SQL> insert into t values(2, '');
1 row created.

SQL> select * from t;
    ID      AGE
---------- ----------
     1        0
     2

既然不是收尾,是需要让当前INSERT的记录,判断若是NULL,则更新值为0,是不是需要使用BEFORE INSERT,而不是AFTER INSERT,执行发现报错,

SQL> CREATE OR REPLACE TRIGGER TR1
  BEFORE INSERT ON T
  FOR EACH ROW
BEGIN
  value := 0;
  IF (:NEW.AGE IS NULL) THEN
    :NEW.AGE:=0;
  END IF;
END;
/
Warning: Trigger created with compilation errors.

SQL> show error
Errors for TRIGGER TR1:
LINE/COL ERROR
-------- -----------------------------------------------------------------
2/3     PL/SQL: Statement ignored
2/3     PLS-00321: expression 'VALUE' is inappropriate as the left hand
     side of an assignment statement

调整一下参数值,定义变量,替换常量0,

SQL> CREATE OR REPLACE TRIGGER TR1
  BEFORE INSERT ON T
  FOR EACH ROW
  DECLARE value NUMBER;
BEGIN
  value := 0;
  IF (:NEW.AGE IS NULL) THEN
    :NEW.AGE:=VALUE;
  END IF;
END;
/

SQL> insert into t values (1, '');
1 row created.

SQL> select * from t;
    ID      AGE
---------- ----------
     1        0

实现了最初的需求了,总结一下,使用BEFORE INSERT,插入之前,判断NEW.AGE是否为空,若是则用变量value=0赋值,此时执行INSERT,就会用0值,而不是原始NULL,进行操作。

若使用AFTER INSERT,我认为可以实现,但要注意避免,ORA-04091错误,感兴趣的朋友可以试一试,要是有结果,可以贴出来,分享一下。

如果您觉得此篇文章对您有帮助,欢迎关注微信公众号:bisal的个人杂货铺,您的支持是对我最大的鼓励!共同学习,共同进步:)

时间: 2024-11-09 00:43:59

一个触发器需求的案例的相关文章

MySQL触发器的应用案例

网友问题:对于MySQL主从复制结构中,如何让Slave过滤delete操作. 问题分析:该问题对应的是保全所有的记录,包括delete的记录. 对于MySQL本身的过滤规则粒度是精确到表的粒度. 主从可以由不同的触发器 问题结果,在slave上建立before触发器,对于delete的记录备份至另一个表. 存在问题,MySQL触发器在高并发下的性能问题. 触发器源代码: CREATE TRIGGER delbak BEFORE DELETE ON tb_name FOR EACH ROW BE

sql数据库 当一张表的内容添加一条时 写一个触发器 使其自动再生成一张

问题描述 sql数据库 当一张表的内容添加一条时 写一个触发器 使其自动再生成一张 1C sql数据库 当一张表的内容添加一条时 写一个触发器 使其自动再生成一张表 但自动生成的表的名字需要是添加的这个表的一个字段的内容 比如说表中添加了一个成员的名字 需要自动生成的表的名字就是这个人名 解决方案 需求可以实现,但没有这么去做的,这个表里有10000条数据时,你生成1万张表???? 建议修改设计,改成其它的方案来实现 解决方案二: Sql Server数据库新增触发器里面可以通过select (

一个开发需求的解决方案 & Oracle临时表介绍

一.开发需求 最近有一个开发需求,大致需要先使用主表,或主表和几张子表关联查询出ID(主键)及一些主表字段,然后再用这些ID查找最多10张表中对应的记录,主表记录数大约2000万,每张子表的记录数均为百万以上,最多可能会有5000万,主表一条数据可能对应子表多条数据.现在开发使用的逻辑是: 1.使用条件查询主表或主表和几张子表(不同场景)符合条件的主表记录ID值及其他一些主表字段项. 2.利用这些主表ID值,分别和几张子表使用IN子句,查询出子表中符合条件的记录项.有几张子表,就执行几次SQL语

写一个触发器,要求达到目标如下:

问题描述 写一个触发器,要求达到目标如下: 1. zk.cm_user_his新增加记录的时候,判断region_code的值,如果是770的,把号码写到zk.temp_test_user_0770的表(没表的话,自己建表) 如果是771的话写到k.temp_test_user_0771,依次类推. 2.zk.cm_user_his是更新记录的操作,也把号码保存到zk.temp_test_user_077x的表. 3.zk.cm_user_his是删除记录的操作,也把号码保存到zk.temp_t

类别-根据以下信息用SQL设计一个触发器和游标功能

问题描述 根据以下信息用SQL设计一个触发器和游标功能 数据项编号 数据项名 数据项含义 与其它数据项的关系 存储结构 别名DI-1 BookID 图书条码号 char(9) 条码号DI-2 BookNo 图书索书号 char(10) 索书号DI-3 BookName 图书名 char(20) 书名DI-4 BookWriter 图书作者 char(8) 作者DI-5 BookPublish 图书出版社 char(20) 出版社DI-6 BookPrice 图书单价 char(7) 单价DI-7

数据库-如何实现一个触发器:在插入一条数据前创建一个表,表名由插入的数据决定?

问题描述 如何实现一个触发器:在插入一条数据前创建一个表,表名由插入的数据决定? MySQL数据库的触发器能不能实现创建表,表名用变量指定? 这个变量该如何表示? 谢谢. 解决方案 可以的,使用动态sql就可以,参考如下链接 http://www.2cto.com/database/201209/156678.html

王永通:分享一个七夕情人节营销案例

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 王永通:分享一个七夕情人节营销案例 2010年8月16号就是我们可爱的七夕情人节了,做为站长的我们一定都嗅到了这一块大蛋糕了吧.怎么样让自己的网站也在这个佳节过得愉快呢?下面我分享一个七夕情人节营销案例,希望对大家能有所启发. 以下为引用的内容: 以下为引用的内容: 厦门问问七夕情人节活动免费送Q币啦!!还不快来领? 活动对象: 1.活动会员

MySQL slave_net_timeout参数解决的一个集群问题案例_Mysql

[背景]    对一套数据库集群进行5.5升级到5.6之后,alter.log 报warning异常.    复制代码 代码如下: 2015-02-03 15:44:51 19633 [Warning] Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using

哪位同学能分享一个CloudFoundary PaaS入门案例?

问题描述 2012年的云开发者大会的时候本人有幸受邀参加,作为第一个开源的云服务平台,MarkLucovsky讲了一个入门案例,分别从应用,实例和服务几个方面简单理解其技术原理整理了一下请大家探讨.1.技术架构:scalableruntime/framework&service2.技术平台:node,ruby,java,postgres,redis,mysql3.实例,还是经典的HelloWorld:--------C-Code------------$cathw.c#include<std