游标(cursor) 是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
MySQL游标只能用于存储过程。
使用游标涉及几个明确的步骤:
使用DECLARE语句创建。DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子句。存储过程处理完成后,游标就消失了,因为它局限于存储过程中。
在游标定义后,可以打开它。打开和关闭使用OPEN、CLOSE关键字。游标关闭后必须重新打开才能使用,使用声明过的游标不需要再次声明。
隐含关闭 如果不明确关闭游标,MySQL会在到达END语句时自动关闭它。
在一个游标被打开后,可以使用FETCH语句分别访问它的每一行。FETCH指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条FETCH语句检索下一行。
下面的语句定义了名为ordernumbers的游标,使用了可以检索所有订单的SELECT语句(FETCH用来检索当前行的order_num列,自动从第一行开始,存储到一个名为o的局部声明变量中,对检索出的数据不做任何处理):
mysql> 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 the cursor
-> FETCH ordernumbers INTO o;
->
-> -- Close the cursor
-> CLOSE ordernumbers;
->
-> END;
-> //
Query OK, 0 rows affected (0.01 sec)
下面的例子将对取出来的数据进行某种实际的处理:
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;
查看存储过程创建的表:
mysql> CALL processorders();
Query OK, 1 row affected (0.03 sec)
mysql> SELECT * FROM ordertotals;
+-----------+---------+
| order_num | total |
+-----------+---------+
| 20005 | 158.86 |
| 20009 | 40.78 |
| 20010 | NULL |
| 20006 | 58.30 |
| 20007 | 1060.00 |
| 20008 | 132.50 |
| 20008 | 132.50 |
+-----------+---------+
7 rows in set (0.00 sec)
MySQL语句在需要时被执行,存储过程也是。
触发器 是在MySQL响应以下任意语句而自动执行的一条MySQL语句(或位于BEGIN和END语句之间的一组语句):
其他MySQL语句不支持触发器。
只有表支持触发器,视图不支持。
触发器的某些用途是执行对要插入表中的值的检查,或对更新中涉及的值进行计算。
创建触发器时,需要给出4条信息:
触发器名必须在每个表唯一,但不是在每个数据库中唯一。但最好还是在数据库范围内使用唯一的触发器名。
要创建触发器或删除触发器,使用 CREATE TRIGGER或 DROP TRIGGER声明:
CREATE
[DEFINER = user]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
[trigger_order]
trigger_body
trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }
trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name
可以为给定的表定义具有相同触发事件和动作时间的多个触发。例如,BEFORE UPDATE一个表可以有两个触发器。默认情况下,具有相同触发事件和动作时间的触发将按照其创建顺序进行激活。要影响触发器的顺序,请在其后指定一个子句,该子句FOR EACH ROW指示FOLLOWS或, PRECEDES并且还具有相同的触发器事件和动作时间的现有触发器的名称。使用 FOLLOWS,新触发器将在现有触发器之后激活。使用PRECEDES,新触发器将在现有触发器之前激活。
例如,以下触发器定义BEFORE INSERT为account表定义了另一个 触发器 :
mysql> CREATE TRIGGER ins_transaction BEFORE INSERT ON account
FOR EACH ROW PRECEDES ins_sum
SET
@deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
@withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
Query OK, 0 rows affected (0.01 sec)
MySQL在触发器执行过程中按以下方式处理错误:
在触发器主体内,使用OLD和NEW关键字可以访问受触发器影响的行中的列。
触发器可以按名称包含对表的直接引用:
CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(
a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
b4 INT DEFAULT 0
);
delimiter |
CREATE TRIGGER testref BEFORE INSERT ON test1
FOR EACH ROW
BEGIN
INSERT INTO test2 SET a2 = NEW.a1;
DELETE FROM test3 WHERE a3 = NEW.a1;
UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
END;
|
delimiter ;
INSERT INTO test3 (a3) VALUES
(NULL), (NULL), (NULL), (NULL), (NULL),
(NULL), (NULL), (NULL), (NULL), (NULL);
INSERT INTO test4 (a4) VALUES
(0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
假设您将以下值插入表中 test1,如下所示:
mysql> INSERT INTO test1 VALUES
(1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8 Duplicates: 0 Warnings: 0
结果四个表包含以下数据:
mysql> SELECT * FROM test1;
+------+
| a1 |
+------+
| 1 |
| 3 |
| 1 |
| 7 |
| 1 |
| 8 |
| 4 |
| 4 |
+------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM test2;
+------+
| a2 |
+------+
| 1 |
| 3 |
| 1 |
| 7 |
| 1 |
| 8 |
| 4 |
| 4 |
+------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
| 2 |
| 5 |
| 6 |
| 9 |
| 10 |
+----+
5 rows in set (0.00 sec)
mysql> SELECT * FROM test4;
+----+------+
| a4 | b4 |
+----+------+
| 1 | 3 |
| 2 | 0 |
| 3 | 1 |
| 4 | 2 |
| 5 | 0 |
| 6 | 0 |
| 7 | 1 |
| 8 | 1 |
| 9 | 0 |
| 10 | 0 |
+----+------+
10 rows in set (0.00 sec)
事务处理(transaction processing) 可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。
事务(transaction) 指一组SQL语句;
回退(rollback) 指撤销指定SQL语句的过程;
提交(commit) 指将未存储的SQL语句结果写入数据库表;
保留点(savepoint) 指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)。
管理事务处理的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该。
MySQL使用下面的语句标识事务的开始:
START TRANSACTION
事务处理用来管理INSERT、UPDATE和DELETE语句。不能回退SELECT语句(这样做也没有什么意义),不能回退CREATE或DROP操作。事务处理块中可以使用这两条语句,但是如果执行回退,它们不会被撤销。
为了支持回退部分事务,必须能在事务处理块中合适位置放置占位符,这样可以回退到某个占位符。
为了创建占位符,可使用SAVEPOINT语句:
SAVEPOINT delete1;
...
ROLLBACK TO delete1;
默认的MySQL行为都是自动提交所有更改的。为指示MySQL不自动提交更改,需使用以下语句:
SET autocommit=0;
使用时START TRANSACTION,自动提交保持禁用状态,直到您使用COMMIT或结束事务 ROLLBACK。然后,自动提交模式将恢复为之前的状态。