视图、储存过程、游标和触发器
MySQL 5添加了对视图、存储过程、游标和触发器的支持,因此,本文内容适用于MySQL 5及以后的版本。
视图
其实就是封装的图表使用方法
视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
理解视图的最好方法是看一个例子。我们之前用 SELECT 语句从三个表中检索数据。
SELECT cust_name, cust_contact
FROM customers,orders,orderitems
WHERE customers,cust_id = orders.cust_id
AND orderitems.order_num = orders.order_num
AND prod_id = 'TNT2';
此查询用来检索订购了某个特定产品的客户。任何需要这个数据的人都必须理解相关表的结构,并且知道如何创建查询和对表进行联结。为了检索其他产品(或多个产品)的相同数据,必须修改最后的 WHERE 子句。
现在,假如可以把整个查询包装成一个名为 productcustomers 的虚拟表,则可以如下轻松地检索出相同的数据:
SELECT cust_name,cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';
为什么要创建这个视图呢?下面是视图的一些常见应用。
- 重用SQL语句。
- 简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必知道它的基本查询细节。
- 使用表的组成部分而不是整个表。
- 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限。
- 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。
在视图创建之后,可以用与表基本相同的方式利用它们。可以对视图执行 SELECT 操作,过滤和排序数据,将视图联结到其他视图或表,甚至能添加和更新数据(添加和更新数据存在某些限制。关于这个内容稍后还要做进一步的介绍)。
重要的是知道视图仅仅是用来查看存储在别处的数据的一种设施。视图本身不包含数据,因此它们返回的数据是从其他表中检索出来的。在添加或更改这些表中的数据时,视图将返回改变过的数据。
不过视图也存在性能问题。因为视图不包含数据,所以每次使用视图时,都必须处理查询执行时所需的任一个检索。如果你用多个联结和过滤创建了复杂的视图或者嵌套了视图,可能会发现性能下降得很厉害。因此,在部署使用了大量视图的应用前,应该进行测试。
下面是关于视图创建和使用的一些最常见的规则和限制。
- 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)。
- 对于可以创建的视图数目没有限制。
- 为了创建视图,必须具有足够的访问权限。这些限制通常由数据库管理人员授予。
- 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图。
- ORDER BY 可以用在视图中,但如果从该视图检索数据 SELECT 中也含有 ORDER BY ,那么该视图中的 ORDER BY 将被覆盖。
- 视图不能索引,也不能有关联的触发器或默认值。
- 视图可以和表一起使用。例如,编写一条联结表和视图的 SELECT语句。
那怎么样创建这个视图呢?
- 视图用 CREATE VIEW 语句来创建。
- 使用 SHOW CREATE VIEW viewname ;来查看创建视图的语句。
- 用 DROP 删除视图,其语法为 DROP VIEW viewname;。
- 更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE ORREPLACE VIEW。如果要更新的视图不存在,则第 2 条更新语句会创建一个视图;如果要更新的视图存在,则第 2 条更新语句会替换原有视图。
利用视图简化复杂的联结
视图的最常见的应用之一是隐藏复杂的SQL,这通常都会涉及联结。请看下面的例子:
CREATE VIEW productcustomers AS
SELECT cust_name, cust_contact, prod_id
FROM customers,orders,orderitems
WHERE customers,cust_id = orders.cust_id
AND orderitems.order_num = orders.order_num
这条语句创建一个名为 productcustomers 的视图,它联结三个表,以返回已订购了任意产品的所有客户的列表。如果执行 SELECT * FROM productcustomers ,将列出订购了任意产品的客户。
为检索订购了产品 TNT2 的客户,可如下进行:
SELECT cust_name,cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';
这条语句通过 WHERE 子句从视图中检索特定数据。在MySQL处理此查询时,它将指定的 WHERE 子句添加到视图查询中的已有WHERE 子句中,以便正确过滤数据。
可以看出,视图极大地简化了复杂SQL语句的使用。利用视图,可一次性编写基础的SQL,然后根据需要多次使用。创建不受特定数据限制的视图是一种好办法。例如,上面创建的视图返回生产所有产品的客户而不仅仅是 生产TNT2 的客户。扩展视图的范围不仅使得它能被重用,而且甚至更有用。这样做不需要创建和维护多个类似视图。
用视图重新格式化检索出的数据
下列语句在单个组合计算列中返回供应商名和位置:
SELECT Concat(RTrim(vend_name),'(',RTrim(vend_country),'(')
AS vend_title
FROM vendors
ORDER BY vend_name;
输出为:
+------------------------+
| vend_title |
+------------------------+
| ACME(USA( |
| Anvils R Us(USA( |
| Furball Inc.(USA( |
| Jet Set(England( |
| Jouets Et Ours(France( |
| LT Supplies(USA( |
+------------------------+
现在,假如经常需要这个格式的结果。不必在每次需要时执行联结,创建一个视图,每次需要时使用它即可。为把此语句转换为视图,可按如下进行:
CREATE VIEW vendorlocations AS
SELECT Concat(RTrim(vend_name),'(',RTrim(vend_country),'(')
AS vend_title
FROM vendors
ORDER BY vend_name;
这条语句使用与以前的 SELECT 语句相同的查询创建视图。为了检索出以创建所有邮件标签的数据,可如下进行
SELECT *
FROM vendorlocations;
+------------------------+
| vend_title |
+------------------------+
| ACME(USA( |
| Anvils R Us(USA( |
| Furball Inc.(USA( |
| Jet Set(England( |
| Jouets Et Ours(France( |
| LT Supplies(USA( |
+------------------------+
用视图过滤不想要的数据
视图对于应用普通的 WHERE 子句也很有用。例如,可以定义customeremaillist 视图,它过滤没有电子邮件地址的客户。为此目的,可使用下面的语句:
CREATE VIEW customermillist AS
SELECT cust_id, cust_name,cust_email
FROM customers
WHERE cust_email IS NOT NULL
这样子没有存邮件的值就不会被检索出来。
在使用视图来检索他:
SELECT *
FROM customermillist ;
使用视图与计算字段
视图对于简化计算字段的使用特别有用。下面这个语句检索某个特定订单中的物品,计算每种物品的总价格:
SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
From orderitems
WHERE order_num = 20005;
+---------+----------+------------+----------------+
| prod_id | quantity | item_price | expanded_price |
+---------+----------+------------+----------------+
| ANV01 | 10 | 5.99 | 59.90 |
| ANV02 | 3 | 9.99 | 29.97 |
| TNT2 | 5 | 10.00 | 50.00 |
| FB | 1 | 10.00 | 10.00 |
+---------+----------+------------+----------------+
现在将其转换为视图:
CREATE VIEW orderitemsexpanded AS
SELECT order_num,
prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
FROM orderitems;
为检索订单 20005 的详细内容(上面的输出),如下进行:
SELECT *
FROM orderitemsexpanded
WHERE order_num = 20005;
+-----------+---------+----------+------------+----------------+
| order_num | prod_id | quantity | item_price | expanded_price |
+-----------+---------+----------+------------+----------------+
| 20005 | ANV01 | 10 | 5.99 | 59.90 |
| 20005 | ANV02 | 3 | 9.99 | 29.97 |
| 20005 | TNT2 | 5 | 10.00 | 50.00 |
| 20005 | FB | 1 | 10.00 | 10.00 |
+-----------+---------+----------+------------+----------------+
更新视图
通常,视图是可更新的(即,可以对它们使用 INSERT 、 UPDATE 和DELETE )。更新一个视图将更新其基表(可以回忆一下,视图本身没有数据)。如果你对视图增加或删除行,实际上是对其基表增加或删除行。
但是,并非所有视图都是可更新的。基本上可以说,如果MySQL不能正确地确定被更新的基数据,则不允许更新(包括插入和删除)。这实际上意味着,如果视图定义中有以下操作,则不能进行视图的更新:
- 分组(使用 GROUP BY 和 HAVING );
- 联结;
- 子查询;
- 并;
- 聚集函数( Min() 、 Count() 、 Sum() 等);
- DISTINCT;
- 导出(计算)列。
换句话说,本章许多例子中的视图都是不可更新的。这听上去好像是一个严重的限制,但实际上不是,因为视图主要用于数据检索。
存储过程
其实就是封装函数
处理数据库问题时,经常会有一个完整的操作需要多条语句才能完成。例如,考虑以下的情形。
- 为了处理订单,需要核对以保证库存中有相应的物品。
- 如果库存有物品,这些物品需要预定以便不将它们再卖给别的人,并且要减少可用的物品数量以反映正确的库存量。
- 库存中没有的物品需要订购,这需要与供应商进行某种交互。
- 关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的客户。
执行这个处理需要针对许多表的多条MySQL语句。此外,需要执行的具体语句及其次序也不是固定的,它们可能会(和将)根据哪些物品在库存中哪些不在而变化。
除了编写条件语句以外,我们可以创建存储过程。存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批文件,虽然它们的作用不仅限于批处理。
为什么要使用存储过程
既那么为什么要使用存储过程呢?下面列出一些主要的理由。
- 通过把处理封装在容易使用的单元中,简化复杂的操作(正如前面例子所述)。
- 由于不要求反复建立一系列处理步骤,这保证了数据的完整性。如果所有开发人员和应用程序都使用同一(试验和测试)存储过程,则所使用的代码都是相同的。这一点的延伸就是防止错误。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。
- 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。这一点的延伸就是安全性。通过存储过程限制对基础数据的访问减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。
- 提高性能。因为使用存储过程比使用单独的SQL语句要快。
- 存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。
换句话说,使用存储过程有3个主要的好处,即简单、安全、高性能。显然,它们都很重要。不过,在将SQL代码转换为存储过程前,也必须知道它的一些缺陷。
- 一般来说,存储过程的编写比基本SQL语句复杂,编写存储过程需要更高的技能,更丰富的经验。
- 你可能没有创建存储过程的安全访问权限。许多数据库管理员限制存储过程的创建权限,允许用户使用存储过程,但不允许他们创建存储过程
使用存储过程
执行存储过程
MySQL称存储过程的执行为调用,因此MySQL执行存储过程的语句为 CALL 。 CALL 接受存储过程的名字以及需要传递给它的任意参数。请看以下例子:
CALL productpricing(@pricelow,@pricehigh,@priceaverage);
其中,执行名为 productpricing 的存储过程,它计算并返回产品的最低、最高和平均价格。
创建存储过程
这个 productpricing 的创建代码为:
CREATE PROCEDURE productpricing()
BEGIN
SELECT Avg(prod_price) AS priceaverage
FROM products;
END;
需注意,如果你使用mysql的命令行客户机,需要为存储过程设定暂时的分隔符号(默认是分号)。具体是在开头加上 DELIMITER // 表示使用 // 作为分隔符,在结尾在加上 DELIMITER ; 来改回去。除 ** 符号外,任何字符都可以用作语句分隔符。
删除存储过程
使用 DROP PROCEDURE 删除存储过程
DROP PROCEDURE productpricing;
使用 DROP PROCEDURE IF EXISTS 当且仅当过程存在时删除,减少报错
使用参数
带参数的 productpricing 函数如下:
CREATE PROCEDURE productpricing(
OUT pl DECIMAL(8,2),
OUT ph DECIMAL(8,2),
OUT pa DECIMAL(8,2)
)
BEGIN
SELECT Min(prod_price) INTO pl
FROM products;
SELECT Max(prod_price) INTO ph
FROM products;
SELECT Avg(prod_price) INTO pa
FROM products;
END;
呼出这个存储过程,并查看数值
CALL productpricing(@pricelow,@pricehigh,@priceaverage);
SELECT @pricelow,@pricehigh,@priceaverage;
还有一种使用 IN 和 OUT 参数的方法,我们写一个 ordertotal 函数接受订单号并返回该订单的合计:
CREATE PROCEDURE ordertotal(
IN onumber INT,
OUT ototal DECIMAL(8,2),
)
BEGIN
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO ototal;
END;
调用这种需要输入的存储过程,过程如下:
CALL ordertotal(20005,@ototal);
建立智能存储过程
迄今为止使用的所有存储过程基本上都是封装MySQL简单的 SELECT 语句。虽然它们全都是有效的存储过程例子,但它们所能完成的工作你直接用这些被封装的语句就能完成。只有在存储过程内包含业务规则和智能处理时,它们的威力才真正显现出来。
考虑这个场景。你需要获得与以前一样的订单合计,但需要对合计增加营业税,不过只针对某些顾客(或许是你所在州中那些顾客)。那么,你需要做下面几件事情:
- 获得合计(与以前一样);
- 把营业税有条件地添加到合计;
- 返回合计(带或不带税)。
存储过程的完整工作如下:
-- 最好写上注释
-- NAME: ordertotal
-- Parameters: onumber = order number
-- taxable = 0 if not taxable, 1 if taxable
-- ototal = order total variable
create procedure ordertotal(
in onumber int,
in taxable boolean,
out ototal decimal(8,2)
) COMMENT 'obtain order total, optionally adding tax'
begin
-- 用declare语句定义两个局部变量。declare要求指定变量名和数据类型。
declare total decimal(8,2);
declare taxrate int default 6;
select sum(item_price*quantity)
from orderitems
where order_num = onumber
into total;
-- if语句检查taxable是否为真(非零为真,零为假),如果为真,则增加营业税到局部变量total中。
if taxable then
select total+(total*taxrate/100) into total;
end if;
select total into ototal;
end;
下面则是调用方法
-- 调用该存储过程,参数为0表示不增加营业税
call ordertotal(20005, 0, @total)
-- 调用该存储过程,参数为1表示增加营业税
call ordertotal(20005, 1, @total)
检查存储过程
为显示用来创建一个存储过程的 CREATE 语句,使用 SHOW CREATE PROCEDURE 语句
为了获得包括何时、由谁创建等详细信息的存储过程列表,使用 SHOW PROCEDURE STATUS LIKE 。
游标
MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行(零行或多行)。使用简单的 SELECT 语句,例如,没有办法得到第一行、下一行或前10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。
有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因。游标(cursor)是一个存储在MySQL服务器上的数据库查询,它不是一条 SELECT 语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。MySQL游标只能用于存储过程(和函数)
使用游标
使用游标涉及几个明确的步骤。
- 在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的 SELECT 语句。
- 一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT 语句把数据实际检索出来。
- 对于填有数据的游标,根据需要取出(检索)各行。
- 在结束游标使用时,必须关闭游标。
游标用 DECLARE 语句创建(参见第23章)。 DECLARE 命名游标,并定义相应的 SELECT 语句,根据需要带 WHERE 和其他子句。例如,下面的语句定义了名为 ordernumbers 的游标,使用了可以检索所有订单的 SELECT 语句。
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
OPEN ordernumbers;
CLOSE ordernumbers;
END;
在声明游标后,可根据需要频繁地打开和关闭游标。在游标打开后,可根据需要频繁地执行取操作。
游标用 OPEN CURSOR 语句来打开,游标处理完成后,应当使用CLOSE CURSOR 关闭游标。CLOSE 释放游标使用的所有内部内存和资源,因此在每个游标不再需要时都应该关闭。在一个游标关闭后,如果没有重新打开,则不能使用它。但是,使用声明过的游标不需要再次声明,用 OPEN 语句打开它就可以了。如果你不明确关闭游标,MySQL将会在到达 END 语句时自动关闭它。
使用游标数据
在一个游标被打开后,可以使用 FETCH 语句分别访问它的每一行。FETCH 指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条 FETCH 语句检索下一行(不重复读取同一行)。
第一个例子从游标中检索单个行(第一行):
create procedure processorders()
begin
--declare local variables
declare o int;
--declare the cursor
declare ordernumbers cursor for select order_num from orders;
--open the cursor
open ordernumbers;
--get order number
fetch ordernumbers into o;
--close the cursor
close ordernumbers;
end;
这条语句中fetch用来检索当前行的order_num列(自动从第一行开始)到一个名为o的局部声明变量中;对检索出的数据不做任何处理。
再看一个例子,循环检索数据,从第一行到最后一行:
create procedure processorders() --创建存储过程
begin
--declare local variables
declare done boolean default 0;
declare 0 int;
--declare the cursor
declare ordernumbers cursor
for
select order_num from orders; --结果集
--declare continue handler
declare continue handler for sqlstate '02000' set done=1; --在这里,done被设置为结束时为真
--open the cursor
open ordernumbers;
--loop through all rows
repeat
--get order number
fetch ordernumbers into0;
--end of loop
until done end repeat;
--close the cursor
close ordernumbers;
end;
这个例子与前一个例子一样,使用fetch检索当前order_num到声明的名为0的变量中。但区别在于:这个例子中fetch是在repeat内,因此它反复执行到done为真(由until done end repeat;规定)。
为了使它起作用,用一个default 0(假、不结束)定义变量done;在语句(declare continue handler for sqlstate ‘02000’ set done=1;)中done被设置为真。SQLSTATE ‘02000’ 是一个未找到条件,当 REPEAT 由于没有更多的行供循环而不能继
续时,出现这个条件。
PS:用declare语句定义的局部变量必须在定义任意游标或句柄之前定义,而句柄必须在游标之后定义。
MySQL还支持循环语句,它可用来重复执行代码,直到使用leave语句手动退出为止;通常repeat语句的语法使它更适合于对游标进行循环。
为了把这些内容组织起来,下面给出我们的游标存储过程样例的更进一步修改的版本,这次对取出的数据进行某种实际的处理:
DELIMITER //
CREATE PROCEDURE processorders()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8, 2);
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
-- Create a table to store the results
CREATE TABLE IF NOT EXISTS ordertotals (order_num INT, total DECIMAL(8, 2));
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- Get the total for this order
CALL ordertotal(o, 1, t);
-- insert order and total into ordertotals
INSERT INTO ordertotals(order_num, total)
VALUES(o, t);
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE ordernumbers;
END//
DELIMITER ;
在这个例子中,我们增加了另一个名为 t 的变量(存储每个订单的合计)。此存储过程还在运行中创建了一个新表(如果它不存在的话),名为 ordertotals 。这个表将保存存储过程生成的结果。 FETCH像以前一样取每个 order_num ,然后用 CALL 执行另一个存储过程来计算每个订单的带税的合计(结果存储到 t )。最后,用 INSERT 保存每个订单的订单号和合计。
此存储过程不返回数据,但它能够创建和填充另一个表,可以用一条简单的 SELECT 语句查看该表:
-- CALL processorders();
SELECT *
FROM ordertotals;
4、使用游标的几个注意事项
①在使用游标前,必须声明(定义)它;这个过程实际上没有检索数据,只是定义要使用的select语句;
②一旦声明,则必须打开游标以供使用(这个过程用前面定义的select语句把数据实际检索出来);
③对于填有数据的游标,根据需要取出(检索)各行;
④结束游标时,必须关闭游标。
触发器
MySQL语句在需要时被执行,存储过程也是如此。但是,如果你想要某条语句(或某些语句)在事件发生时自动执行,怎么办呢?例如:
- 每当增加一个顾客到某个数据库表时,都检查其电话号码格式是否正确,州的缩写是否为大写;
- 每当订购一个产品时,都从库存数量中减去订购的数量;
- 无论何时删除一行,都在某个存档表中保留一个副本。
所有这些例子的共同之处是它们都需要在某个表发生更改时自动处理。这确切地说就是触发器。触发器是MySQL响应以下任意语句而自动执行的一条MySQL语句(或位于 BEGIN 和 END 语句之间的一组语句):DELETE ; INSERT ; UPDATE 。其他MySQL语句不支持触发器。
创建触发器
在创建触发器时,需要给出4条信息:
- 唯一的触发器名;
- 触发器关联的表;
- 触发器应该响应的活动( DELETE 、 INSERT 或 UPDATE );
- 触发器何时执行(处理之前或之后)。
MySQL中,触发器名必须在每个表中唯一,但不限制每个数据库中唯一,即:同一数据库中两个表可以具有相同名字的触发器(其他的DBMS中不被允许)。
触发器使用 create teigger 语句创建,下面是一个简单的例子:
create trigger newproduct after insert on products
for each row select 'product added';
这里创建了一个newproduct的新触发器,给出了after insert,所以此触发器在insert语句成功执行后执行;其中还指定了for each row,因此代码对每个插入行执行。
PS:只有表才支持触发器,视图不支持(临时表也不支持)。
触发器按每个表每个事件每次的定义,每个表每个事件每次只允许一个触发器;因此每个表最多支持6个触发器(每条insert、update和delete的之前和之后),单一触发器不能与多个事件或多个表关联。
如果before触发器失败,则MySQL将不执行请求的操作;此外,如果before触发器或语句本身失败,MySQL将不执行after触发器(如果有的话)。
删除触发器
删除触发器使用drop trigger语句,例如:
drop trigger newproduct;
PS:触发器不能更新或覆盖;为了修改一个触发器,必须先删后建。
使用触发器
insert触发器
使用insert触发器,需要知道以下三点:
-
在insert触发器代码内,可引用一个名为new的虚拟表,访问被插入的行;
-
在before insert触发器内,new中的值也可以被更新(允许更改被插入的值);
-
对于auto increment列,new在insert执行之前包含0,在insert执行之后包含新的自动生成值;
例子如下:
create trigger neworder after insert on orders
for each row select new.order_num;
此代码创建一个名为neworder的触发器,按照after insert on order执行;在插入一条新的数据orders表时,MySQL生成一个新订单号并保存到order_num中;
触发器从new.order_num取这个值并返回它;此触发器必须按照after insert执行,因为在before insert语句执行之前,新order_num还没生成。
PS:通常将before用于数据验证和净化(目的是保证插入表中的数据确实是需要的数据)。这个规则也适用于update触发器。
delete触发器
使用delete触发器,需要知道以下两点:
-
在delete触发器代码内,可以引用一个名为old的虚拟表,访问被删除的行;
-
old中的值全都是只读的,不能更新;
下面是一个使用old保存将要被删除的行到一个存档表中:
create trigger deleteorder before delete on orders
for each row
begin
insert into archive_orders(order_num,order_date,cust_id)
values(OLD_order_num, OLD_order_date, OLD_cust_id);
end;
此代码在任意orders表中的数据被删除前执行此触发器;它使用一条insert语句将old中的值(要被删除的数据)保存到一个名为archive_orders的存档表中;
上面的例子中,使用begin end块的好处是触发器能容纳多条SQL语句。
PS:使用before delete触发器的优点:如果由于某些原因,数据不能存档,delete本身将被放弃。
update触发器
使用update触发器,需要知道以下三点:
-
在update触发器代码内,可以引用一个名为old的虚拟表访问以前(update语句前)的值,引用一个名为new的虚拟表访问新更新的值;
-
在before update触发器中,new中的值可能也被更新(允许更改将要用于update语句中的值);
-
old中的值全都是只读的,不能更新;
下面的例子,保证了州名称缩写总是大写:
create trigger updateevendor before update on vendors
for each row set.vend_state = upper(new.vend_state);
这个例子中,每次更新一个行时,new.vend_state中的值(将用来更新表行的值)都用upper(new.vend_state)替换。
关于触发器一些必须知道的知识:
-
创建触发器可能需要特殊的安全访问权限,但触发器的执行是自动的;如果insert、update、delete语句可以执行,则相应触发器也能执行;
-
应该用触发器来保证数据的一致性(大小写、格式等);有点在于它总是进行这种处理,而且是透明的进行,与客户机应用无关;
-
触发器的一种非常有意义的使用是创建审计跟踪。使用触发器,把更改记录到另一个表非常容易;
-
MySQL触发器不支持call语句,即不能从触发器内调用存储过程,所需的存储过程代码需要复制到触发器内。