`
Programmer_ZhangDi
  • 浏览: 15025 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

触发器过程

阅读更多
触发器过程

PL/pgSQL 可以用于定义触发器过程。 一个触发器过程是用 CREATE FUNCTION 命令创建的, 创建的形式是一个不接受参数并且返回 trigger 类型的函数。 请注意该函数即使在 CREATE TRIGGER 声明里声明为准备接受参数, 它也必需声明为无参数 — 触发器的参数是通过 TG_ARGV 传递的,下面有描述。

在一个 PL/pgSQL 函数当做触发器调用的时候, 系统会在顶层的声明段里自动创建几个特殊变量。有如下这些:

 

 

NEW

数据类型是 RECORD; 该变量为INSERT/UPDATE 操作时保存行(ROW)一级的触发器新的数据库行。 在语句级别的触发器里,这个变量是 NULL

OLD

数据类型是 RECORD; 该变量为 INSERT/UPDATE 操作时保存行(ROW)一级的触发器新的数据库行。 在语句级别的触发器里,这个变量是 NULL

TG_NAME

数据类型是 name;该变量包含实际触发的触发器名。 fired.

TG_WHEN

数据类型是 text;是一个由触发器定义决定的字符串, 要么是 BEFORE 要么是 AFTER

TG_LEVEL

数据类型是 text;是一个由触发器定义决定的字符串, 要么是 ROW 要么是 STATEMENT

TG_OP

数据类型是 text;是一个说明触发触发器的操作的字符串, 可以是 INSERTUPDATE 或者 DELETE

TG_RELID

数据类型是 oid;是导致触发器调用的表的对象标识(OID)。

TG_RELNAME

数据类型是 name;是激活触发器调用的表的名称。

TG_NARGS

数据类型是 integer; 是在CREATE TRIGGER 语句里面赋予触发器过程的参数的个数。

TG_ARGV[]

数据类型是 text 的数组;是 CREATE TRIGGER语句里的参数。 下标从 0 开始记数.非法下标(小于 0 或者大于等于 tg_nargs)导致返回一个 NULL 值。

 

一个触发器函数必须返回 NULL 或者是 一个与导致触发器运行的表的记录/行完全一样的结构的数据。

BEFORE触发的行级别的的触发器可以返回一个 NULL,告诉触发器管理器忽略对该行剩下的操作 (也就是说,随后的触发器将不再执行,并且不会对该行产生INSERT/UPDATE/DELETE动作)。 如果返回了一个非 NULL 的行,那么将继续对该行数值进行处理。 请注意,返回一个和原来的NEW不同的行数值将修改那个将插入或更新的行。 我们可能用一个值直接代替NEW里的某个数值并且返回之,或者我们也可以构建一个完全新的记录/行再返回。

BEFORE 或者 AFTER语句级别的触发器, 或者一个AFTER 行级别的触发器的返回值将总是被忽略; 它们也可以返回 NULL 来忽略返回值。不过,任何这种类型的触发器仍然可以 通过抛出一个错误来退出整个触发器操作。

Example 36-2 显示了一个 PL/pgSQL 写的触发器过程的例子。

Example 36-2. 一个PL/pgSQL触发器过程

下面的例子触发器的作用是:任何时候表中插入或更新了行, 当前的用户名和时间都记录入行中。 并且它保证给出了雇员名称并且薪水是一个正数。

CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp () RETURNS trigger AS $emp_stamp$
BEGIN
-- 检查是否给出了 empname 和 salary
IF NEW.empname ISNULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary ISNULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- 我们必须付帐给谁?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- 记住何时何人的薪水被修改了
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

另外一个向表里记录变化的方法涉及创建一个新表,然后为后来发生的每次插入、更新或者删除动作保存一行。 这个方法可以当作对一个表的审计。 Example 36-3 显示了一个 PL/pgSQL 写的审计触发器过程的例子。

Example 36-3. 一个用于审计的 PL/pgSQL 触发器过程

这个例子触发器保证了在 emp 表上的任何插入, 更新或者删除动作都被记录到了 emp_audit 表里(也就是,审计)。 当前时间和用户名会被记录到数据行里,以及还有执行的操作。

CREATE TABLE emp (
empname           text NOT NULL,
salary            integer
);
CREATE TABLE emp_audit(
operation         char(1)   NOT NULL,
stamp             timestamp NOT NULL,
userid            text      NOT NULL,
empname           text      NOT NULL,
salary integer
);
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
BEGIN
--
-- 在 emp_audit 里创建一行,反映对 emp 的操作,
-- 使用特殊变量 TG_OP 获取操作类型。
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
RETURN NEW;
END IF;
RETURN NULL; -- 忽略结果,因为它是个 AFTER 触发器
END;
$emp_audit$ LANGUAGE plpgsql;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW EXECUTE PROCEDURE process_emp_audit()
;

触发器的一个用途是维持另外一个表的概要。生成的概要可以用于在某些查询中代替原始表 — 通常可以大大缩小运行时间。 这个技巧经常用于数据仓库,这个时候,需要测量的表(叫事实表)可能会非常巨大。 Example 36-4 演示了一个 PL/pgSQL 触发器过程的例子, 它为某个数据仓库的一个事实表维护一个概要表。

Example 36-4. 一个维护概要表的 PL/pgSQL 触发器过程

下面的模式有一部分是基于 Ralph Kimball 的The Data Warehouse Toolkit 里面的 Grocery Store 例子。

--
-- 主表 - 时间维以及销售事实。
--
CREATE TABLE time_dimension (
time_key                    integer NOT NULL,
day_of_week                 integer NOT NULL,
day_of_month                integer NOT NULL,
month                       integer NOT NULL,
quarter                     integer NOT NULL,
year                        integer NOT NULL
);
CREATE UNIQUE INDEX time_dimension_key ON time_dimension(time_key);
CREATE TABLE sales_fact (
time_key                    integer NOT NULL,
product_key                 integer NOT NULL,
store_key                   integer NOT NULL,
amount_sold                 numeric(12,2) NOT NULL,
units_sold                  integer NOT NULL,
amount_cost                 numeric(12,2) NOT NULL
);
CREATE INDEX sales_fact_time ON sales_fact(time_key);
--
-- 摘要表 - 根据时间的销售。
--
CREATE TABLE sales_summary_bytime (
time_key                    integer NOT NULL,
amount_sold                 numeric(15,2) NOT NULL,
units_sold                  numeric(12) NOT NULL,
amount_cost                 numeric(15,2) NOT NULL
);
CREATE UNIQUE INDEX sales_summary_bytime_key ON sales_summary_bytime(time_key);
--
-- 在 UPDATE,INSERT,DELETE 的时候根新概要字段的函数和触发器。
--
CREATE OR REPLACE FUNCTION maint_sales_summary_bytime() RETURNS TRIGGER AS $maint_sales_summary_bytime$
DECLARE
delta_time_key          integer;
delta_amount_sold       numeric(15,2);
delta_units_sold        numeric(12);
delta_amount_cost       numeric(15,2);
BEGIN
-- 计算增/减量。
IF (TG_OP = 'DELETE') THEN
delta_time_key = OLD.time_key;
delta_amount_sold = -1 * OLD.amount_sold;
delta_units_sold = -1 * OLD.units_sold;
delta_amount_cost = -1 * OLD.amount_cost;
ELSIF (TG_OP = 'UPDATE') THEN
-- 禁止改变 time_key 的更新 -
-- (可能并不是很强制,因为 DELETE + INSERT 是大多数可能
-- 产生的修改)。
IF ( OLD.time_key != NEW.time_key) THEN
RAISE EXCEPTION 'Update of time_key : % -> % not allowed', OLD.time_key, NEW.time_key;
END IF;
delta_time_key = OLD.time_key;
delta_amount_sold = NEW.amount_sold - OLD.amount_sold;
delta_units_sold = NEW.units_sold - OLD.units_sold;
delta_amount_cost = NEW.amount_cost - OLD.amount_cost;
ELSIF (TG_OP = 'INSERT') THEN
delta_time_key = NEW.time_key;
delta_amount_sold = NEW.amount_sold;
delta_units_sold = NEW.units_sold;
delta_amount_cost = NEW.amount_cost;
END IF;
-- 用新数值更新概要行。
UPDATE sales_summary_bytime
SET amount_sold = amount_sold + delta_amount_sold,
units_sold = units_sold + delta_units_sold,
amount_cost = amount_cost + delta_amount_cost
WHERE time_key = delta_time_key;
-- There might have been no row with this time_key (e.g new data!).
IF (NOT FOUND) THEN
BEGIN
INSERT INTO sales_summary_bytime (
time_key,
amount_sold,
units_sold,
amount_cost)
VALUES (
delta_time_key,
delta_amount_sold,
delta_units_sold,
delta_amount_cost
);
EXCEPTION
--
-- 捕获两个事务维一个新 time_key 增加数据的冲突条件
--
WHEN UNIQUE_VIOLATION THEN
UPDATE sales_summary_bytime
SET amount_sold = amount_sold + delta_amount_sold,
units_sold = units_sold + delta_units_sold,
amount_cost = amount_cost + delta_amount_cost
WHERE time_key = delta_time_key;
END;
END IF;
RETURN NULL;
END;
$maint_sales_summary_bytime$ LANGUAGE plpgsql;
CREATE TRIGGER maint_sales_summary_bytime
AFTER INSERT OR UPDATE OR DELETE ON sales_fact
FOR EACH ROW EXECUTE PROCEDURE maint_sales_summary_bytime();
分享到:
评论

相关推荐

    oracle触发器调用存储过程

    ### Oracle触发器调用存储过程 #### 知识点概览 1. **触发器基本概念** 2. **存储过程简介** 3. **触发器如何调用存储过程** 4. **示例代码详解** 5. **注意事项** 6. **自主事务(Autonomous Transaction)** 7. ...

    数据库实验报告-存储过程、触发器

    通过这些实验步骤,学生能够深入理解存储过程和触发器的工作原理及其在实际数据库应用中的作用。同时,这些操作也展示了如何在数据库管理系统中编写和管理这些数据库对象,有助于提升数据库设计和管理能力。

    Oracle触发器与存储过程高级编程-第3版itpub.rar

    《Oracle触发器与存储过程高级编程》第3版是一本深入探讨Oracle数据库中触发器和存储过程技术的专业书籍。在Oracle数据库系统中,触发器和存储过程是数据库管理员和开发人员进行复杂业务逻辑处理和数据管理的重要...

    数据库存储过程及触发器

    数据库 存储过程 触发器 基本简介

    存储过程触发器 游标

    在数据库管理中,存储过程和触发器是两个关键概念,它们极大地增强了数据库的功能性和灵活性。游标则在处理逐行数据时发挥着重要作用。本文将深入探讨存储过程、触发器和游标,以及它们在实际应用中的综合使用。 ...

    mysql触发器+存储过程

    MySQL数据库系统提供了强大的数据库管理功能,其中两个关键的特性是触发器(Triggers)和存储过程(Stored Procedures)。这两者都是数据库编程的重要组成部分,能够帮助用户实现更复杂的数据操作和业务逻辑。 首先...

    SQL Server数据库实验_存储过程与触发器设计.docx

    在SQL Server数据库中,存储过程和触发器是两种重要的数据库对象,它们在数据管理与业务逻辑处理中扮演着关键角色。 存储过程是预编译的SQL语句集合,它可以包含一系列的数据操作,如查询、更新、插入和删除等。...

    易飞系统触发器储存过程函数视图

    这些是平时个人兴趣爱好收集到的易飞ERP系统触发器/函数/储存过程/视图集。适合人群:系统管理员,运维专员或鼎捷顾问等

    触发器调用存储过程

    触发器调用存储过程

    图书管理系统触发器和存储过程.pdf

    "图书管理系统触发器和存储过程" 图书管理系统触发器和存储过程是数据库管理系统中两个重要的概念。触发器(Trigger)是一种特殊的存储过程,用于自动执行某些操作,以维护数据的一致性和完整性。存储过程(Stored ...

    存储过程和触发器语法详细讲解

    在数据库管理中,存储过程和触发器是两个重要的概念,它们在数据操作和业务逻辑实现中扮演着不可或缺的角色。本文将深入探讨这两个概念,提供详尽的语法解析,并结合实例来帮助理解它们的工作原理和应用。 一、存储...

    Oracle数据库创建存储过程和触发器

    Oracle 数据库创建存储过程和触发器 Oracle 数据库创建存储过程和触发器是高级数据库开发设计的重要组成部分。存储过程和触发器是一种特殊类型的数据库对象,它们可以实现复杂的业务逻辑和数据处理操作。 存储过程...

    sql存储过程和触发器

    SQL存储过程和触发器是数据库管理系统中用于实现特定功能的重要工具,主要应用于SQL Server 2000等数据库系统。 存储过程(Stored Procedure)是一组预先编译的SQL语句集合,它们被存储在数据库中,可以被多次调用...

    oracle触发器与存储过程高级编程

    在Oracle中,触发器和存储过程是两个关键的数据库编程元素,它们在数据管理和业务流程自动化中发挥着重要作用。本节将深入探讨“Oracle触发器与存储过程高级编程”的相关知识。 **触发器(Triggers)** 触发器是一...

    实验六:创建存储过程和触发器1

    实验六的目的是深入理解并掌握SQL Server中的存储过程和触发器。存储过程是预编译的SQL语句集合,它们可以被多次调用,提高了代码的重用性和执行效率,同时降低了网络流量。以下是关于存储过程和触发器的详细解释: ...

    存储过程和触发器的编程

    在数据库管理领域,存储过程和触发器是两个重要的概念,它们在数据操作和业务逻辑实现中扮演着不可或缺的角色。下面将详细阐述这两个概念及其在实际应用中的重要性。 首先,我们来了解一下存储过程(Stored ...

    触发器与存储过程实验

    ### 触发器与存储过程实验 #### 一、实验目的 本次实验旨在通过实践操作,让学生更加深入地理解触发器和存储过程的概念及其在实际数据库管理中的应用。通过具体的实验任务,帮助学生掌握如何创建、修改和使用这些...

    GBase8s 存储过程和触发器测试方法

    在GBase8s数据库系统中,存储过程和触发器是重要的数据库对象,它们允许数据库管理员和开发者实现更复杂的业务逻辑和数据操作。本篇将详细阐述如何在GBase8s中进行存储过程的测试,以及相关知识。 首先,存储过程是...

Global site tag (gtag.js) - Google Analytics