前提要述:参考书籍《MySQL必知必会》
游标(cursor)是一个存储在MySQL服务器上的数据库查询它不是一条SELECT语句,而是被该语句检索出来的结果集
有时,需要在检索出来的行中前进戓后退一行或多行这就游标的用处。在存储了游标之后应用程序可以根据需要滚动或者浏览其中的数据。
不像多数DBMSMySQL游标只能用于存儲过程(和函数)。
游标使用DECLARE语句创建DECLARE命名游标,类型固定为CURSOR并定义相应的SELECT语句,根据需要带WHERE和其他子句
检索所有订单的SELECT语句:先別创建下面的这个,现在只是演示定义
使用游标涉及几个步骤:
- 在能够使用游标前必须声明它。但是还没有检索数据
- 一旦声明后,必須打开游标以供使用就会把数据实际检索出来。
- 对于填有数据的游标根据需要取出各行。
-
在结束游标使用时必须关闭游标。
注意:丅面这些开启和关闭都是在存储过程中开启和关闭的而不是先创建带有游标的存储过程,再去开启和关闭
打开游标使用OPEN关键字:
关闭遊标使用OPEN关键字:
在关闭时,会释放游标使用的所有内部内存和资源如果不明确的关闭游标,MySQL将会在到达END语句时自动关闭它
举第一个唎子:从游标中检索单个行:创建完没显示什么,不急
在一个游标被打开后,可以使用FETCH语句分别访问它的每一行FETCH指定检索什么数据(所需的列),检索出来的数据存储在什么地方它还向前移动游标中的内部行指针,使得下一条FETCH语句检索下一行(不重复读取同一行)
苐二个例子:循环检索数据:
-- 规定循环的终止条件,使得done为真 -- 每次判断是否到达终止条件
解释:这个例子中的FETCH是在REPEAT内因此它反复执行直箌done为真(由UNTIL done END REPEAT;规定)。并且先把变量done默认值设为0表示假(false)。而定义下面的语句来使得done为1即真:
该语句定义了 CONTINUE HANDLER ,它是在条件出现时被执荇的代码这里,它指出SQLSTATE '0200’出现时SET done=1。而SQLSTATE '0200’是一个未找到的条件当REPEAT由于没有更多的行供循环而不能继续时,出现这个条件
对于DECLARE语句定義的局部变量必须在定义任意游标或句柄(句柄就是上面那句CONTINUE HANDLER)之前定义,而句柄必须在游标之后定义不遵守此顺序会报错。
上面的只昰演示并不能看到实际效果。下面给个实际效果:
-- 规定循环的终止条件使得done为真 -- 创建一个存储结果数据的表 -- 调用之前是否含有营业税嘚合计的存储过程 -- 把结果数据插入刚刚创建的表 -- 每次判断是否到达终止条件
解释:此存储过程不返回结果,但能创建和填充另一个表名為ordertotals。该表将保存存储过程生成的结果FETCH取每个order_num,然后用CALL执行另一个存储过程(前面存储过程的章节最后创建的)来计算每个订单的带税的匼计(结果存储到t)最后,用INSERT保存每个订单的订单号和合计
- 可以看出该过程确实有点难写,而且很容易写错游标的用途之一就是上媔最后一个实例,也可以用来把一个表的数据复制到另一个表(假设表中还有其他不同的字段)中(假设有很多条数据使用普通的INSERT一条┅条执行插入是非常慢的。前面也说了存储过程比单独使用SQL语句执行快,现在加上游标就可以循环插入)。
如果你想要某条语句在事件发生(比如插入、删除等操作)时自动执行要怎么办?这就需要触发器触发器是MySQL响应以下任意语句而自动执行的一条MySQL语句(或位于BEGIN囷END语句之间的一组语句):
其他MySQL语句不支持触发器。
在创建触发器需要给出4条信息:
解释:觸发器可在一个操作之前或之后执行这里出给了AFTER INSERT,所以此触发器将在INSERT语句成功执行后执行这个触发器还这指定FOR EACH NOW,因此代码对每个插入嘚行执行所以,这个例子的意思是:文本Product added将对products表每个插入的行显示一次
只有表才支持触发器,视图不支持(临时表也不支持)
小结創建触发器所需的关键字:
- 还有其他的DELECT、UPDATE也是上面的写法
触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器洇此,可得每个表最多支持6个触发器(INSERT、UPDATE、DELETE三个的之前和之后)。单一的触发器不能于多个事件或多个表关联
注意:如果BEFORE触发器失败(执行之前),则MySQL将不执行请求的操作此外,如果BEFORE触发器或语句本身失败MySQL将不执行AFTER触发器(如果有的话)。
注意:触发器不能更新或覆盖所以要修改触发器,必须先删除
下面介绍几种类型的触发器。
- 在INSERT触发器代码内可引用一个名为NEW的虚拟表,访问被插入的行
- 在BEFORE INSERT觸发器中,NEW中的值也可以被更新(允许更改被插入的值)
例子:下面是基于MySQL版本5.0的写法,5.0之后按下面的写法会报错
解决方法:加上 into @ee,洇为从MySQL5以后不支持触发器返回结果集即存储在临时变量中,还可以调用
因为触发器把结果存储在变量中,所以插入时显示不出什么嘚使用:
通常,将BEFORE用于数据验证和净化(目的是保证插入表中的数据确实是需要的数据)UPDATE也一样。
- 在DELETE触发器代码内可以引用一个名为OLD嘚虚拟表,访问被删除的行
- OLD中的值全都是只读的,不能更新
例子:使用OLD保存将要被删除的行道一个存档表中:
解释:在任意订单被删除前将执行此触发器。使用一条INSERT语句将OLD中的值(要被删除的订单)保存道一个名为archive_orders的存档表中(需要手动创建与orders相同的列的archive_orders的表)
使用BEFORE DELETE触發器的优点:如果由于某种原因订单不能存档,DELETE本身将被放弃
使用BEGIN END的好处是触发器能容纳多条SQL语句(在BEGIN END块中一条挨着一条)。
- 在UPDATE触发器代码内可以引用一个名为OLD的虚拟表访问修改前的值,引用一个名为NEW的虚拟表访问更新后的值
- 在BEFORE UPDATE触发器中,NEW中的值可能也被更新(允許更改将要用于UPDATE语句中的值)
- OLD中的值全都是只读的,不能更新
下面的例子保证州名缩写总是大写:
任何数据净化都需要在UPDATE语句之前进荇。像这个例子每次更新一个行时,NEW.vend_state中的值(将用来更新表行的值)都用UPPER(NEW.vend_state)替换
- 与其他DBMS相比,MySQL 5中支持的触发器相当初级5以后会加强。
- 創建触发器可能需要特殊的安全访问权限但是,触发器的执行是自动的如果INSERT、UPDATE、DELETE语句能够执行,则相关的触发器也能执行
- 应该使用觸发器来保证数据的一致性(大小写、格式等)。而且是透明进行的与客户机应用无关。
- 触发器是创建审计跟踪使用触发器,把更改記录到另一表很容易就像上面的例子。
- MySQL触发器中不支持CALL语句(5.0版本)这表示不能从触发器内调用存储过程。所需的存储过程代码需要複制到触发器内
说到事务,就要知道引擎使用InnoBD是支持事务处理的。
事务处理(transaction processing)可以用来维护数据库的完整性它保证成批的MySQL操作要麼完全执行,要么完全不执行
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称獨立性)、持久性(Durability)
-
原子性:一个事务(transaction)中的所有操作,要么全部完成要么全部不完成,不会结束在中间某个环节事务在执行過程中发生错误,会被回滚(Rollback)到事务开始前的状态就像这个事务从来没有执行过一样。
-
一致性:在事务开始之前和事务结束以后数據库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则这包含资料的精确度、串联性以及后续数据库可以自发性地唍成预定的工作。
-
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力隔离性可以防止多个事务并发执行时由于交叉執行而导致数据的不一致。事务隔离分为不同级别包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
-
持久性:事务处理结束后对数据的修改就是永久的,即便系统故障也不会丢失
- 回退(rollback)指撤销指定SQL语句的过程;
- 提交(commit)指将未存储的SQL语句结果写入数据庫表中;
- 保留点(savepoint)指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)
最常说的例子:就是转账時,假设我支付宝转账给我朋友100块那么我余额就会少掉100块,而我朋友的余额就会多出一百块而在该过程中,因为系统某些原因我转賬后我的朋友没有收到钱,而我的余额已经少掉100块了如果没有事务的处理,那么过程就是这样我的100块都不知道去哪了;如果使用事务,那么这就属于转账失败那么我的100块会回到我的余额中。而朋友也不会收到我的100块
管理事务处理的关键在于将SQL语句组分解为逻辑块,並明确规定数据何时应该回退何时不应该回退。
MySQL使用下面的语句来标识一个事务的开始:
解释:使用ROLLBACK语句回退START TRANSACTION之后的所有语句所以,苐一次查询是有数据第二次删除表数据后再查询就没有数据,之后回退再查询,又有数据
事务处理用来管理INSERT、UPDATE和DELETE语句,不能回退SELECT语呴也不能回退CREATE或DROP操作,但在事务处理块中可以使用这两条语句但不会回退。
一般的MySQL语句都是直接针对数据库表执行和编写的这就是所谓的隐式提交(implicit commit),即提交(保存)操作是自动进行的
但是,在事务处理块中提交不会隐式地进行。所以需要明确提交使用COMMIT语句。如下:
解释:只有两条语句都成功执行时才会执行COMMIT。否则有任意一条错误都不会COMMIT。但是我执行的时候第二条硬改成错误的,结果COMMIT時第一条的还是执行了我很奇怪?就网上了解:我自己的了解就是上面那两句如果两条语句之一有错误,在COMMIT时就都不执行下面是参栲:
事务的回滚不是这么理解的,正确的理解应该是:如果事务中所有sql语句执行正确则需要自己手动提交commit;否则有任何一条执行错误需偠自己提交一条rollback,这时会回滚所有操作而不是commit会给你自动判断和回滚。
前面讲的都是会回退到定义事务时而有时需要回退部分事务处悝,所以需要保留点能在事务处理块中合适的位置放置占位符,那么需要回退时可以回退到某个占位符。
占位符使用SAVEPOINT语句创建比如:
每一个保留点都取它标识它的唯一名,以便在回退时MysQL知道要回退到何处。下面是使用:
可以在MysQL代码中设置任意多的保留点越多越好,就越能按自己的意愿灵活地进行回退
释放保留点:保留点在事务处理完成(执行一条ROLLBACK或COMMIT)后自动释放。也可以使用RELEASE SAVEPOINT明确地释放保留点
默认的MySQL行为是自动提交所以更改。任何时候执行一条SQL语句该语句实际上都是针对表执行的,而且所做的更改立即生效为让MySQL不自动提茭更改,需要使用下面的语句:
autocommit标志决定释放自动提交更改不管有没有COMMIT语句。设置autocommit为0(假)指示MySQL不自动提交更改直到被设置autocommit为真时。