GBase 8s SQL 指南:教程———13 创建和使用触发器

本章描述CREATE TRIGGER语句的每个组成部分的用途,说明触发器的一些用法,并描 述将SPL例程用作触发器的优点。

此外,本章还描述可在视图上定义的INSTEAD OF触发器。

SQL触发器是驻留在数据库中的一种机制。具有使用许可权的任何用户都可以使用它。SQL 触发器指定当数据操纵语言(DML)操作(INSERT、SELECT、DELETE或UPDATE语 句)时,数据库服务器应自动执行一个或多个附加操作。对于在视图上定义的触发器,视 图基本表上的触发操替换触发事件。对于表或视图上的触发器,触发操作可以是INSERT、 DELETE、UPDATE、EXECUTE PROCEDURE 或 EXECUTE FUNCTION 语句。

GBase 8s还支持用C或Java™编写的用户定义的例程作为触发操作。

有关如何撰写C UDR以获取有关触发器事件的元数据信息,请参阅《GBase 8s DataBlade API程序员指南》。

13.1何时使用触发器

因为触发器驻留在数据库中,且具有必需特权的任何用户都可以使用它,所以触发器允许 您编写可供多个应用程序使用的一组SQL语句。它可在多个程序需要执行同一数据库操 作时避免冗余码。

可使用触发器执行下列操作以及在此列表中找不到的其它操作:

•在数据库中创建活动的审计跟踪。例如:可通过更新审计表的确认信息来跟踪对订 单的更新。

•实现业务规则。例如:可以确定何时订单超出客户的信用卡限制并对此情况显示一 条消息。

•派生表内或数据内未提供的其它数据。例如:当对items表的quantity列进行更新 时,可以计算对total_price列的相应调整。

•强制执行引用完整性。例如:在删除客户时,可以使用触发器来删除orders表中具 有相同客户号的相应行。

13.2如何创建触发器

使用CREATE TRIGGER语句定义新触发器。CREATE TRIGGER语句是数据定义语句, 它将称为触发操作的SQL语句与表上的诱发事件相关联。当发生诱发操作时,它触发存 储在数据库中的关联SQL语句。

在本示例中,触发事件是引用items表中的quantity列的UPDATE语句。下图说明激活触 发器的DML操作(称为触发器事件)与触发操作之间的关系。

图:触发事件和触发操作

UPDATE

rtem nom quantily

totaLprice

2 3

15.00

EXECUTE PROCEDURE

3 1

236.00

upd_items

A 4

100.00

280.00

trigger event

CREATE TRIGGER语句由执行下列操作的子句组成:

• 声明触发器名称。

•指定在指定表或试图上作为触发事件的DML操作。

•定义该事件触发器的SQL操作。

FOR EACH ROW 触发操作中讨论了称为REFERENCING子句的可选子句。

要创建触发器,使用DB-Access或某个SQL API。本节描述当您在DB-Access中使用交互 查询语言选项输入CREATE TRIGGER语句时的用法。在SQL API中,您在语句前加上 将该语句标识为嵌入式语句的符号或关键字。

    1. 1声明触发器名称

触发器名称标识触发器,且该名称在数据库的触发器名称中必须唯一。在语句中,触发名 称跟在CREATE TRIGGER后面。与任何SQL标识一样,该名称最长为128个字节, 以字母开始并由字母、数字和下划线(_)组成。在以下示例中,所示的CREATE TRIGGER 语句部分声明了触发器的名称upqty:

CREATE TRIGGER upqty – declare trigger name

    1. 2指定触发器事件

触发器事件是一种激活触发器的DML语句。当对表执行此类型的语句时,数据库服务器 执行组成触发操作的SQL语句。对于表,触发器事件可以是INSERT、SELECT、 DELETE或UPDATE语句。对于UPDATE或SELECT触发事件,可以指定表中一列 或多列可以激活触发器。可以在同一表上定义多个INSERT、SELECT、DELETE和 UPDATE触发器,也可以在同一视图上定义多个INSERT、DELETE和UPDATE触发 器。

在当前数据库中的一个表或一个视图上,只能创建一个触发器。触发器不能引用远程表或 视图。

在以下CREATE TRIGGER语句片段中,触发事件定义为更新items表中quantity列:

CREATE TRIGGER upqty

UPDATE OF quantity ON items – an UPDATE trigger event

语句的这一部分标识在其上定义触发器的表。如果触发器事件为插入或删除操作,那么仅 需要语句的类型和表名,如以下示例所示:

CREATE TRIGGER ins_qty

NSERT ON items – an INSERT trigger event

    1. 3定义触发操作

触发操作是当触发事件发生时执行的SQL语句。触发操作可以由INSERT、DELETE、 UPDATE、EXECUTE FUNCTION 和 EXECUTE PROCEDURE 语句组成。但是,除了指 定要执行什么操作外,还必须就触发语句何时执行这些操作。您有以下选择:

•在触发语句执行之前

•在触发语句执行之后

•针对触发语句作用的每一行

表上的单个触发器可以为上述每一时间定义操作。

要定义触发操作,指定该操作何时发生,然后提供要执行的SQL语句,可使用关键字 BEFORE、AFTER或FOR EACH ROW指定该操作何时发生。然后是括在圆括号中的触 发操作。下面的触发操作定义指定在触发语句之前执行SPL例程upd_items_pl :

BEFORE(EXECUTE PROCEDURE upd_items_p1) – a BEFORE action

  1. 2.4 完整的 CREATE TRIGGER 语句

要定义完整的CREATE TRIGGER语句,将触发器名称子句、触发事件子句和触发操作子 句组合起来。下面的CREATE TRIGGER语句是将前例中的语句的各个组成部分组合在一 起的结果。每次更新items表的quantity列时,此触发器将执行SPL例程upd_items_p1 o

CREATE TRIGGER upqty

UPDATE OF quantity ON items

BEFORE(EXECUTE PROCEDURE upd_items_p1);

如果在数据库服务器处理CREATE TRIGGER语句时,触发器定义中的数据库对象(例如, 本例中的SPL例程upd_items_p1)不存在,那么返回错误。

13.3使用触发操作

要有效地使用触发器,需要理解触发语句和生成的触发操作之间的关系。在指定发生触发 操作的时间(即,BEFORE、AFTER或FOR EACH ROW)时定义此关系。

    1. 1 BEFORE 和 AFTER 触发操作

在触发事件之前或之后发生的触发操作仅执行一次。BEFORE触发操作在触发语句之前执 行,即在触发器事件发生之前执行,AFTER触发操作在触发语句操作完成之后执行。即使 触发语句不处理任何行,BEFORE和AFTER触发操作也会执行。

除其它用法外,还可以使用BEFORE和AFTER触发操作来确定触发语句的效果。例如, 在更新items表的quantity列之前,可以调用SPL例程upd_items_p1来计算表中所有项的 订购总数,如下例所示,该过程将总数存储在名为old_qty的全局变量中。

CREATE PROCEDURE upd_items_p1()

DEFINE GLOBAL old_qty INT DEFAULT 0;

LET old_qty = (SELECT SUM(quantity) FROM items);

END PROCEDURE;

在完成触发更新之后,可以再次计算总数来看看更改了多少。下面的SPL例

程upd_items_p2再次计算了 quantity的总数并将结果存储在局部变量new_qty中。然后, 它将new_qty与全局变量old_qty相比较,以查看所有订单的总量的增长是否超过50%。 若是,该过程将使用RAISE EXCEPTION语句来模拟SQL错误。

CREATE PROCEDURE upd_items_p2()

DEFINE GLOBAL old_qty INT DEFAULT 0;

DEFINE new_qty INT;

LET new_qty = (SELECT SUM(quantity) FROM items);

IF new_qty > old_qty * 1.50 THEN

RAISE EXCEPTION -746, 0, ‘Not allowed - rule violation’;

END IF

END PROCEDURE;

下列触发器调用upd_items_pl和upd_items_p2以防止对items表的quantity列点进行异常 更新:

CREATE TRIGGER up_items

UPDATE OF quantity ON items BEFORE(EXECUTE PROCEDURE upd_items_p1()) AFTER(EXECUTE PROCEDURE upd_items_p2());

如果更新使得对所有项的订购总量增长超过50%,那么upd_items_p2中的RAISE EXCEPTION语句终止该触发器,并显示错误。当进行事务记录的数据库服务器中的触发 器发生故障时,数据库服务器会回滚超过语句和触发操作进行的更改。有关触发器发生故 障时所发生的情况的更多信息,请参阅《GBase 8s SQL指南:语法》中的CREATE TRIGGER 语句。

    1. 2 FOR EACH ROW 触发操作

FOR EACH ROW触发操作对触发语句所作用的每一个行执行一次。例如,如果触发语句 有下列语法,将对manu_cod e列的值为’KAR’的items表中的每一行执行一次FOR EACH ROW 触发操作:

UPDATE items SET quantity = quantity * 2 WHERE manu_code = ‘KAR’;

如果触发事件不处理任何行,将不会执行FOR EACH ROW触发操作。

对于表上的触发器,如果触发事件为SELECT语句,那么该触发器称为选择触发器,并且 触发操作在完成时对检索到的行的所有处理之后再执行。但是,触发操作可能不会立即执 行;原因是会对查询返回的行的每一个实例执行FOR EACH ROW操作。例如,在带有 ORDER BY子句的SELECT语句中,必须先根据WHERE子句限定所有行,它们才能排 序并返回。

REFERENCING 子句

当创建FOR EACH ROW触发操作时,通常必须在触发操作语句中指示您引用的是触发语 句生效之前还是之后的列值。例如:假定您想要跟踪对items表的quantity列的更新。为此, 创建下表以记录该活动:

CREATE TABLE log_record

(item_num SMALLINT,

ord_num INTEGER,

username CHARACTER(8),

update_time DATETIME YEAR TO MINUTE,

old_qty SMALLINT,

new_qty SMALLINT);

要为此表中的old_qty和new_qty列提供值,必须能够引用items表中的quantity的旧值和 新值。即,触发语句作用之前和之后的值。REFERENCING子句可使您做的这一点。

REFERENCING子句允许您创建可与列名组合起来的两个前缀,一个用于引用列的旧值, 另一个用于引用列的新值。这些前缀称为相关名。可以根据您的要求创建一个或两个相关 名。您指出使用关键字OLD和NEW创建的哪个相关名。下面的REFERENCING子句 创建相关名pre_upd和post_upd来引用行中的旧值和新值:

REFERENCING OLD AS pre_upd NEW AS post_upd

当更新items表中的某行中的quantity时,以下触发操作将在log_record中创建一行。

INSERT语句引用item_num和order_num列的旧值并引用quantity列的新值和旧值。

FOR EACH ROW(INSERT INTO log_record

VALUES (pre_upd.item_num, pre_upd.order_num, USER,

CURRENT, pre_upd.quantity, post_upd.quantity));

在REFERENCING子句中定义的相关名应用于触发语句作用的所有行。

重要:如果引用未被相关名限定的列名,数据库服务器不会专门在触发表的定义中搜索该列。 必须总是将相关名与FOR EACH ROW触发操作中的SQL语句中的列名结合使用,除非该语 句独立有效,而与触发操作无关。有关更多信息,请参阅《GBase 8s SQL指南:语法》中的 CREATE TRIGGER 语句。

WHEN条件

作为表上的触发器的选项,可在WHEN子句之前加上触发操作以使该操作依赖于测试结 果。WHEN子句由关键字WHEN以及跟随在其后的括在圆括号中的条件语句所组成。在 CREATE TRIGGER 语句中,WHEN 子句跟在关键字 BEFORE、AFTER 或 OR EACH ROW之后,触发操作列表之前。

当WHEN条件存在时,如果它求值为true,那么按触发操作的出现顺序执行这些操作。 如果WHEN条件求值为false或unknown,那么不执行触发操作列表中的操作。如果触发 器指定FOR EACH ROW,那么还将针对每一行对条件进行求值。

在下面的触发器示例中,仅当WHEN子句中的条件为true (即,如果更新后单价高于更 新前单价的两倍)时才执行触发操作:

CREATE TRIGGER up_price

UPDATE OF unit_price ON stock

REFERENCING OLD AS pre NEW AS post

FOR EACH ROW WHEN(post.unit_price > pre.unit_price * 2) (INSERT INTO warn_tab

VALUES(pre.stock_num, pre.manu_code, pre.unit_price, post.unit_price, CURRENT));

有关 WHEN条件的更多信息,请参阅《GBase 8s SQL指南:语法》中的CREATE TRIGGER 语句。

    1. 3将SPL例程用作触发操作

触发器最强大的功能可能是能够将SPL例程作为触发操作进行调用,调用SPL例程的 EXECUTE PROCEDURE或EXECUTE FUNCTION语句允许您将数据从触发表传递至 SPL例程,还允许您使用由SPL例程返回的数据更新触发表。SPL还允许您定义变量、 对其指定数据、进行比较以及使用过程语句来完成触发操作内的复杂任务。

将数据传至SPL例程

可以在EXECUTE PROCEDURE或EXECUTE FUNCTION 语句的参数列表中将数据传 递至SPL例程。以下示例中的EXECUTE PROCEDURE语句将值从items表 的 quantity 和 total_price 列传递至 SPL 例程 calc_totpr:

CREATE TRIGGER upd_totpr

UPDATE OF quantity ON items

REFERENCING OLD AS pre_upd NEW AS post_upd

FOR EACH ROW(EXECUTE PROCEDURE calc_totpr(pre_upd.quantity, post_upd.quantity, pre_upd.total_price) INTO total_price);

将数据传递至SPL例程允许您在该例程执行的操作中使用数据值。

使用SPL

在之前触发列中的EXECUTE PROCEDURE语句调用以下示例所示的SPL例程。在更 新items表中的quantity时,该过程使用SPL计算需要对total_price列作出的更改。该过程 接收quantity的旧值和新值以及total_price的旧值。它用旧的总价除以旧的数值来得出单 价。然后用新的数量乘以单价来得出新的总价。

CREATE PROCEDURE calc_totpr(old_qty SMALLINT, new_qty SMALLINT,

total MONEY(8)) RETURNING MONEY(8);

DEFINE u_price LIKE items.total_price;

DEFINE n_total LIKE items.total_price;

LET u_price = total / old_qty;

LET n_total = new_qty * u_price;

RETURN n_total;

END PROCEDURE;

在本示例中,SPL允许触发器派生不能直接从触发表获得的数据。

用SPL例程中的数据更新非触发列

在触发操作内,EXECUTE PROCEDURE语句的INTO子句允许您更新触发表中的非触发 列。下例中的EXECUTE PROCEDURE语句调用包含INTO子句(该子句引

用 total_price 列)的 calc_totpr SPL 过程:

FOR EACH ROW(EXECUTE PROCEDURE calc_totpr(pre_upd.quantity, post_upd.quantity, pre_upd.total_price) INTO total_price);

更新到total_price中的值是SPL过程结束时由RETURN语句返回的。对触发语句作用的 每一行更新total_price列。

13.4触发器例程

可以定义称为触发器例程专用SPL例程,此类例程只能从触发器操作的FOR EACH ROW 段进行调用。与EXECUTE FUNCTION或EXECUTE PROCEDURE例程可以从触发操作 列表中调用的普通UDR不同,触发器例程包含自己的REFERENCING子句,可用于为 触发操作修改的行中原有列和新列值定义相关名。这些相关名可以在触发器例程中的SPL 语句中引用,为触发操作可在表或视图中修改数据的方式提供更大的灵活性。

触发器例程也可使用称为DELETING、INSERTING、SELECTING和UPDATING触发 器类型的布尔运算符,以标识已调用触发器例程的触发器的类型。触发器例程还可以调 用mi_trigger*例程(有时称为触发器自省类型)来获取关于已调用触发器例程的上下文的 信息。

触发器例程由包含 WITH TRIGGER REFERENCES关键字的EXECUTE FUNCTION 或 EXECUTE PROCEDURE语句调用。这些语句必须从触发操作的FOR EACH ROW 段中调 用触发器例程,而不是从BEFORE或AFTER段中进行调用。

有关支持定义和执行触发器例程的SQL的CREATE FUNCTION、CREATE PROCEDURE , EXECUTE FUNCTION 和 EXECUTE PROCEDURE 语句的语法特征的信 息,请参阅《GBase 8s SQL指南:语法》。有关mi_trigger*例程的更多信息,请参阅《GBase 8s DataBlade API程序员指南》。

13.5表层次结构中的触发器

当您在超表上定义触发器时,表层次结构中的所有子表也会继承该触发器。因此,当您对 层次结构中的表执行操作时,可对层次结构中作为对其定义触发器的表的子表的任何表执 行触发器。

13.6 Select 触发器

当CREATE TRIGGER语句将指定表上的任何查询定义为其触发事件(

SELECT ON table

SELECT ON column-list ON table

)时,生成的触发对象是指定表的Select触发器。同一触发器还可以被包含将此表作为其 基本表的触发列的视图上的查询激活。但是,SELECT语句不能是视图上INSTEAD OF触 发器的触发事件。

如果CREATE TRIGGER语句在嵌入Select触发事件的定义中也包含列列表,并且指定 表上后续查询的投影列表不包含任何指定的列,那么该查询不能是此Select触发器的触发 事件。

警告:

Select触发器不建议用于审计。不要出于执行应用程序指定审计的目的而在表或其列的子 集上尝试创建Select触发器。一般情况下,通过创建Select触发器来跟踪表上的Select 动作的数量,以在每次用户查询某个表时将审计记录插入到审计表中是不可能的。

例如,假设您在表AuditedTable上定义了 Select触发器,且对AuditedTable持有Select特 权的用户发出了以下查询:

SELECT a.* FROM (SELECT * FROM AuditedTable) AS a;

数据库服务器不发出错误,但是AuditedTable上的SELECT触发器不会被此查询激活。包 含集合运算符(例如UNION或INTERSECT)的查询,或者其它Select触发器不支持的 语法,将会被基于Select触发器的审计记录策略无视。

因为执行Select触发器的大量的限制(部分在本章中列出),生成的Select触发操作通 常仅对应于试图枚举的任何逻辑Select事件的子集(它可能为空)。

    1. 1执行触发操作的SELECT语句

当创建select触发器时,仅某些类型的SELECT语句可以执行对该触发器定义的操作。 Select触发器仅当下列类型的SELECT语句执行:

独立的SELECT语句t
SELECT语句的选择列表中的集合子查询
嵌入用户定义例程的SELECT语句
视图
独立SELECT语句

假设您对表定义了下面的Select触发器:

CREATE TRIGGER hits_trig SELECT OF col_a ON tab_a

REFERENCING OLD AS hit

FOR EACH ROW (INSERT INTO hits_log

VALUES (hit.col_a, CURRENT, USER));

当触发列出现在独立SELECT语句的选择列表中时执行Select触发器。以下语句针对数 据库服务器返回的行的每个实例执行hits_tng触发器上的触发操作:

SELECT col_a FROM tab_a;

査询投影列表中的集合子査询

当触发列出现在位于其它SELECT语句的投影列表中的集合子查询中时,将执行Select 触发器。以下语句针对集合子查询返回的行的每个实例执行hits_trig触发器上的触发操作:

SELECT MULTISET(SELECT col_a FROM tab_a) FROM …

嵌入在用户定义例程中的SELECT语句

对嵌入在用户定义的例程(UDR)中的SELECT语句定义的选择触发器仅在以下情况下执 行触发操作:

UDR出现在SELECT语句的选择列表中
UDR 使用 EXECUTE PROCEDURE 语句调用
假设您创建包含语句SELECT col_a FROM tab_a的例程new_proc。下面的每条语句针对嵌 入的SELECT语句所返回行的每一个实例执行hits_trig触发器的触发操作:

SELECT new_proc() FROM tab_b;

EXECUTE PROCEDURE new_proc;

视图

Select触发器对其基础表包含触发列的引用的视图执行触发操作。但是,不能在视图上定 义Select触发器。

假设您创建了下列视图:

CREATE VIEW view_tab AS

SELECT * FROM tab_a;

以下语句针对视图返回的行的每个实例执行hits_trig触发器上的触发操作:

SELECT * FROM view_tab;

SELECT col_a FROM tab_a;

    1. 2执行Select触发器的限制

下列类型的SELECT语句不会触发Select触发器上的任何操作。

触发列不在投影列表中(例如,出现在SELECT语句的WHERE子句中的列不会 执行Select触发器)。
引用远程表的SELECT语句。
SELECT语句调用聚集函数或OLAP窗口聚集函数。
SELECT 语句包含集合运算符或(UNION、UNION ALL、INTERSECT、MINUS 或 EXCEPT)o
SELECT语句包含DISTINCT或UNIQUE关键字。
包含SELECT语句的UDR表达式不在投影列表中。
SELECT语句出现在INSERT INTO语句中。
SELECT语句岀现在滚动游标中。
触发器是级联Select触发器。
级联Select触发器是其操作包含SPL例程的触发器,该例程本身具有触发select 语句。但是,不执行级联Select触发器的操作,数据库服务器也不返回错误。

    1. 3在表层次结构中的表的Select触发器

当您对超表定义select触发器,表层次结构中的所有子表也会继承此触发器。

有关覆盖和禁用继承触发器的信息,请参阅表层次结构中的触发器。

13.7可重入触发器

可重入触发器指的是其中触发操作可引用触发表的情况。换句话说,也就是触发器事件和 触发操作可作用于同一个表。例如,假设下面的UPDATE语句表示触发事件:

UPDATE tabl SET (col_a, col_b) = (col_a + 1, col_b + 1);

以下触发操作是合法的,因为列col_c不是触发事件已更新的列:

UPDATE tab1 SET (col_c) = (col_c + 3);

在前面的示例中,对col_a或col_b的触发操作可能是非法的,因为触发操作不能是引用触 发事件所更新的列的UPDATE语句。

重要:Select触发器不能是可重入触发器。如果触发事件为SELECT语句,那么不能对同一 个表执行触发操作。

有关描述在哪些情况下触发器可为或不可为可重入触发器的规则的列表,请参阅《GBase 8s SQL指南:语法》中的CREATE TRIGGER语句。

13.8视图上的INSTEAD OF触发器

视图是使用CREATE VIEW语句创建并使用SELECT语句定义的虚拟表。每个视图由若 干行列集合组成,它们是在您每次通过查询引用该视图时,由其视图定义中的SELECT语 句返回的。要在视图的基本表中插入、更新或删除行,可以定义INSTEAD OF触发器。

与表上的触发器不同,视图上的INSTEAD OF触发器导致GBase 8s忽略触发事件,而只 执行触发操作。

有关CREATE VIEW语句和INSTEAD OF触发器语法和规则的信息,包括将对视图插入 行的INSTEAD OF触发器的示例,请参阅《GBase 8s SQL指南:语法》。

    1. 1使用INSTEAD OF触发器对视图进行更新

在创建一个或多个表之后(如下例中名为dept和emp的表),然后又创建了基

于dept和emp的视图(如名为manager_info的视图)之后,使用INSTEAD OF触发器更 新该视图。

以下 CREATE TRIGGER 语句创建 manager_info_update,这是一个 INSTEAD OF 触发器, 用来通过manager_info视图更新dept和emp表中的行。

CREATE TRIGGER manager_info_update

INSTEAD OF UPDATE ON manager_info

REFERENCING NEW AS n

FOR EACH ROW

(EXECUTE PROCEDURE updtab (n.empno, n.empname, n.deptno,));

CREATE PROCEDURE updtab (eno INT, ename CHAR(20), dno INT,) DEFINE deptcode INT;

UPDATE dept SET managejnum = eno where deptno = dno;

SELECT deptno INTO deptcode FROM emp WHERE empno = eno;

IF dno !=deptcode THEN

UPDATE emp SET deptno = dno WHERE empno = eno;

END IF;

END PROCEDURE;

在创建了表、视图、触发器和SPL例程以后,数据库服务器将下面的UPDATE语句视作 触发事件:

UPDATE manager_info

SET empno = 3666, empname = “Steve” WHERE deptno = 01;

此触发UPDATE语句不会得到执行,但是此事件将造成执行触发器操作,即调 用updtab() SPL例程。SPL例程中的UPDATE语句将值更新到manager_info视图 的emp和dept基本表中。

13.9跟踪触发操作

如果触发操作并未按您所期望的那样运行,那么将其放置在SPL例程中,并使用SPL TRACE语句来监视其操作。在启动跟踪之前,必须使用SET DEBUG FILE TO语句将输 出定向到文件。

    1. 1 SQL例程中的TRACE语句的示例

以下示例显示了添加到SPL例程items_pct中的TRACE语句。SET DEBUG FILE TO语 句将跟踪输出定向至路径名所指定的文件。TRACE ON语句开始跟踪过程中的语句和变 量。

CREATE PROCEDURE items_pct(mac CHAR(3))

DEFINE tp MONEY;

DEFINE mc_tot MONEY;

DEFINE pct DECIMAL;

SET DEBUG FILE TO ‘pathname’;

TRACE ‘begin trace’;

TRACE ON;

LET tp = (SELECT SUM(total_price) FROM items);

LET mc_tot = (SELECT SUM(total_price) FROM items

WHERE manu_code = mac);

LET pct = mc_tot / tp;

IF pct > .10 THEN

RAISE EXCEPTION -745;

END IF

TRACE OFF;

END PROCEDURE;

CREATE TRIGGER items_ins

INSERT ON items

REFERENCING NEW AS post_ins

FOR EACH ROW(EXECUTE PROCEDURE items_pct (post_ins.manu_code));

    1. 2 TRACE输出的示例

以下示例显示了 items_pct过程中的样本跟踪输出,这些输出出现在SET DEBUG FILE TO 语句所指定的文件中。这些输出显示过程变量、过程参数、返回值和错误代码的值。

trace expression :begin trace

trace on

expression:

(select (sum total_price)

from items)

evaluates to $18280.77 ;

let tp = $18280.77

expression:

(select (sum total_price)

from items

where (= manu_code, mac))

evaluates to $3008.00 ;

let mc_tot = $3008.00

expression:(/ mc_tot, tp)

evaluates to 0.16

let pct = 0.16

expression:。pct, 0.1)

evaluates to 1

expression:(- 745)

evaluates to -745

raise exception :-745, 0,’’

exception : looking for handler

SQL error = -745 ISAM error = 0 error string ==’’

exception : no appropriate handler

有关如何使用TRACE语句诊断SPL例程中的逻辑错误的更多信息,请参阅创建和使用 SPL例程。

13.10生成错误消息

当触发器因为SQL语句而失败时,数据库服务器将返回适用于特定失败原因的SQL错误 号。

当触发操作是SPL例程时,可以使用两个保留错误号的其中之一针对错误情况生成错误消 息。第一个是错误号-745,它具有通用且固定的错误消息。第二个是错误号-746,它允许 您提供最多70字节的信息正文。

  1. 10.1应用固定错误消息

可以将错误号-745应用于并非SQL错误的任何触发器故障。下列固定消息用于此错误: -745 Trigger execution has failed。

可以在SPL中将此消息应用于RAISE EXCEPTION语句。在以下示例中,如 果new_qty大于1.50倍的old_qty,那么生成错误号-745 :

CREATE PROCEDURE upd_items_p2()

DEFINE GLOBAL old_qty INT DEFAULT 0;

DEFINE new_qty INT;

LET new_qty = (SELECT SUM(quantity) FROM items);

IF new_qty > old_qty * 1.50 THEN

RAISE EXCEPTION -745;

END IF

END PROCEDURE

如果您正在使用DB-Access,那么错误-745消息的文本在屏幕的底部显示,如下图所示。

图:带有固定消息的错误消息-745

Press CTRL-W for Help

SQL: New Run Modify Use-editor Output Choose Save Info Drop Exit Modify the current SQL statements using the SQL editor.

-------------------- stores8@myserver------------ Press CTRL-W for Help —­

INSERT INTO items VALUES( 2, 1001,2, ‘HRO’, 1, 126.00);

745: Trigger execution has failed.

如果触发器在SQL API中通过SQL语句调用包含错误的过程,数据库服务器将把SQL 错误变量设置为-745,并将其返回至程序。要显示消息正文,遵循GBase 8s应用程序开发 工具提供的过程以检索SQL错误消息的正文。

  1. 10.2生成可变错误消息

错误号-746允许您提供错误消息的正文。就像前面的示例,如果new_qty大于1.50倍 的old_qty,以下示例也将生成错误。但是,在本例中,错误号为-746,并且消息正文Too many items for Mfr.的项过多是作为RAISE EXCEPTION语句中的第三个参数提供的。有 关此语句的语法和使用的更多信息,请参阅创建和使用SPL例程中的RAISE EXCEPTION 语句。

CREATE PROCEDURE upd_items_p2()

DEFINE GLOBAL old_qty INT DEFAULT 0;

DEFINE new_qty INT;

LET new_qty = (SELECT SUM(quantity) FROM items);

IF new_qty > old_qty * 1.50 THEN

RAISE EXCEPTION -746, 0, ‘Too many items for Mfr.’;

END IF

END PROCEDURE;

如果使用DB-Access提交触发语句,并且如果new_qty大于old_qty,那么您将得到下图显 示的结果。

图:带有用户指定消息正文的错误号-746

Press CTRL-W for Help

SQL: New Run Modify Use-editor Output Choose Save Info Drop

Exit

Modify the current SQL statements using the SQL editor.

如果在SQL API中通过SQL语句调用触发器,那么数据库服务器将sqlcode设置为-746, 并在SQL通信区域(SQL;CA)的sqlerrm字段中返回消息正文。有关如何使用SQL;CA的 更多信息,请参阅您的SQL API出版物。

13.11总结

为介绍触发器,本章讨论了下列主题:

CREATE TRIGGER语句的各个组成部分
•可作为触发事件的DML语句的类型

•可作为触发操作的SQL语句的类型

如何创建BEFORE和AFTER触发操作以及如何使用它们来确定触发语句的影 响
如何创建FOR EACH ROW触发操作,以及如何使用REFERENCING子句引用 触发语句执行之前和之后的列值
•视图上的INSTEAD OF触发器,其触发事件将被忽略,但触发操作可以修改视图 的基本表

•将SPL例程用作触发操作的好处

•将触发例程作为触发操作调用的特殊功能

•在触发操作的执行异常时如何跟踪它们

•如何在触发操作内生成两种类型的错误消息
————————————————
版权声明:本文为CSDN博主「aisirea」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aisirea/article/details/124093275

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值