数据库MySQL的索引、引擎和事务
一、MySQL索引
【1】索引定义
索引,在关系数据库中,是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。
索引提供指向存储在表的指定列中的数据值的指针,然后根据指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。
【2】索引特点
优势
(1)大大加快数据的检索速度;
(2)创建唯一性索引,保证数据库表中每一行数据的唯一性;
(3)加速表和表之间的连接;
(4)在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
劣势
(1)索引需要占物理空间。
(2)当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
【3】索引分类
(1)普通索引:仅加速查询
普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。
因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn)中的数据列创建索引。只要有可能,就应该选择一个数据最整齐、最紧凑的数据列(如一个整数类型的数据列)来创建索引。
格式:
1】ALTER TABLE 表名 ADD INDEX 索引名 (字段);
2】create index 索引名 on 表名(字段);
3】create table 表名(
...............省略................
index 索引名(字段)
)
操作示范
使用格式1】
mysql> alter table student add index stu1(name);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | PRI | NULL | |
| name | char(10) | NO | MUL | NULL | |
| socre | decimal(5,2) | YES | | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
使用格式2】
mysql> create index stu2 on student(socre);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | PRI | NULL | |
| name | char(10) | NO | MUL | NULL | |
| socre | decimal(5,2) | YES | MUL | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
使用格式3】
mysql> create table teacher(
-> id int(5) not null auto_increment,
-> name char(10) not null,
-> address varchar(50) default '无',
-> primary key(id),
-> index teach_index(name)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc teacher;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| name | char(10) | NO | MUL | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
(2)唯一索引:加速查询 + 列值唯一(可以有null)
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
在对该列进行增或改时,首先会检查是否重复,在执行增改操作,否则报出duplica错误,拒绝操作。
格式:
1】ALTER TABLE 表名 ADD unique 索引名(字段);
2】create unique index 索引名 on 表名(字段);
3】create table 表名(
...............省略................
unique key 索引名(字段)
)
操作示范
使用格式1】
mysql> alter table student add unique stu1(name);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | PRI | NULL | |
| name | char(10) | NO | UNI | NULL | |
| socre | decimal(5,2) | YES | | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
使用格式2】
mysql> create unique index stu2 on student(socre);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | PRI | NULL | |
| name | char(10) | NO | UNI | NULL | |
| socre | decimal(5,2) | YES | UNI | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
使用格式3】
mysql> create table teacher(
id int(5) not null auto_increment,
name char(10) not null,
address varchar(50) default '无',
primary key(id),
unique key teach_index(name)
);
Query OK, 0 rows affected (0.01 sec)
mysql> desc teacher;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| name | char(10) | NO | UNI | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
(3)主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。
在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。
格式:
1】ALTER TABLE 表名 ADD PRIMARY KEY ( 字段 );
2】create table 表名(
...............省略................
primary key(字段)
)
操作示范
使用格式1】
mysql> alter table student add primary key(name);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | | NULL | |
| name | char(10) | NO | PRI | NULL | |
| socre | decimal(5,2) | YES | | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
使用格式2】
mysql> create table teacher(
-> id int(5) not null auto_increment,
-> name char(10) not null,
-> address varchar(50) default '无',
-> primary key(id)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> desc teacher;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| name | char(10) | NO | | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
修改主键
mysql> alter table teacher modify id int not null;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc teacher;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | char(10) | NO | | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
删除主键
mysql> alter table student drop primary key;
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
(4)组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。
格式:
1】ALTER TABLE 表名 ADD INDEX 索引名 (字段1,字段2);
2】create index 索引名 on 表名(字段1,字段2);
3】create table 表名(
...............省略................
index 索引名(字段1,字段2)
);
操作示范
使用格式3】
mysql> create table teacher(
id int(5) not null auto_increment,
name char(10) not null,
address varchar(50) default '无',
index teach_index(name,address),
primary key(id)
);
Query OK, 0 rows affected (0.01 sec)
mysql> desc teacher;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| name | char(10) | NO | MUL | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
(5)全文索引:对文本的内容进行分词,进行搜索
1】全文索引技术是搜索引擎的关键技术。试想在1M大小的文件中搜索一个词,可能需要几秒,在100M的文件中可能需要几十秒,如果在更大的文件中搜索那么就需要更大的系统开销,这样的开销是不现实的。所以在这样的矛盾下出现了全文索引技术,有时候有人叫倒排文档技术。
2】解决中文分词是断词问题使“分词”成为全文索引的关键技术。有两种基本的方法:
1)二元法是它把所有有可能的每两两汉字的组合看为一个词组,这样就没有维护词库的开销;
2)词库法是它使用词库中的词作为切分的标准,这样也出现了词库跟不上词汇发展的问题,除非你维护词库。
实际上现在很多著名的搜索引擎都使用了多种分词的办法,比如“正向最大匹配”+“逆向最大匹配”,基于统计学的新词识别,自动维护词库等技术,但是显然这样的技术还没有做到完美。
格式:
1】alter table 表名 add fulltext index 索引名(字段);
2】create table 表名(
...............省略................
fulltext key 索引名(字段)
)
3】create fulltext index 索引名 on 表名(字段);
操作示范
使用格式3】
mysql> create fulltext index stu1 on student(name);
Query OK, 0 rows affected, 1 warning (0.09 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | | NULL | |
| name | char(10) | NO | MUL | NULL | |
| socre | decimal(5,2) | YES | | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
【4】删除索引
(1)方法一drop
mysql> drop index teach_index on teacher;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc teacher;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(5) | NO | PRI | NULL | auto_increment |
| name | char(10) | NO | | NULL | |
| address | varchar(50) | YES | | 无 | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
方法二alter
mysql> alter table student drop index stu2;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(9) | NO | PRI | NULL | |
| name | char(10) | NO | MUL | NULL | |
| socre | decimal(5,2) | YES | | 0.00 | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
【5】查看索引的方法
格式:show index/keys from 表名;
举例
mysql> show index from student;
mysql> show keys from student;
【6】创建索引的原则
(1)适合索引的列是出现在where子句中的列,或者连接子句中指定的列
(2)基数较小的类,索引效果较差,没有必要在此列建立索引,记录为300以上建议建索引
(3)使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
(4)不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
(5) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
(6)较频繁作为查询条件的字段才去创建索引
(7)更新频繁字段不适合创建索引
(8)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
(9)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
(10)定义有外键的数据列一定要建立索引。
【7】主键、外键、候选键、主键索引区别
(1)主键(primarykey)
一张表(关系)的一个列(属性)或多个列可以作为主键,但是前提是让这个列作主键,这个列就能保证该列下的各个行(元组)的值不能相同,比如说你用姓名属性作主键的话,那么这个主键就不一定可以,如果有两个人是同样的名字的话,就不能做到该属性下的各个元组数据的值不同,如果用阿拉伯数字作主键就是一个很好的选择。
(2)外键(foreignkey)
一张表(关系)的列(属性)它同时存在表1和表2中,它不是表1的主键,而是表2的主键,就可以说他是表1的外键。
(3)候选键(CandidateKey)
能唯一标识表(关系)中行(元组)的列(属性),则称该属性为候选键,也称候选关键字或候选码;由此来看候选键可以不只一个,还看一看得出的就是主键同时它也是候选键
二、MySQL引擎
数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程序的要求。使用数据库引擎创建用于联机事务处理或联机分析处理数据的关系数据库。这包括创建用于存储数据的表和用于查看、管理和保护数据安全的数据库对象(如索引、视图和存储过程)。可以使用SQLServerManagementStudio管理数据库对象,使用SQLServerProfiler捕获服务器事件。
【1】引擎分类
(1)myisam引擎
MYISAM是MYSQL的ISAM扩展格式和缺省的数据库引擎。除了提供ISAM里所没有的索引和字段管理的大量功能,MYISAM还使用一种表格锁定的机制,来优化多个并发的读写操作。其代价是你需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的空间。MYISAM还有一些有用的扩展,例如用来修复数据库文件的MYISAMCHK工具和用来恢复浪费空间的MYISAMPACK工具。
MYISAM强调了快速读取操作,这可能就是为什么MYSQL受到了WEB开发如此青睐的主要原因:在WEB开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和INTERNET平台提供商只允许使用MYISAM格式。
(2)innodb引擎
INNODB数据库引擎都是造就MYSQL灵活性的技术的直接产品,这项技术就是MYSQL++ API。在使用MYSQL的时候,你所面对的每一个挑战几乎都源于ISAM和MYISAM数据库引擎不支持事务处理也不支持外来键。尽管要比ISAM和MYISAM引擎慢很多,但是INNODB包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。如前所述,如果你的设计需要这些特性中的一者或者两者,那你就要被迫使用后两个引擎中的一个了。
(3)myisam与innodb区别
(4)存储引擎的选择
如果没有特别的需求,使用默认的Innodb即可。
MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。
Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。
(5)修改和查看存储引擎
修改引擎
mysql> alter table teacher engine=myisam;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
创建引擎
mysql> create table teacher(
id int(5) not null auto_increment,
name char(10) not null,
address varchar(50) default '无',
index teach_index(name,address),
primary key(id)
)engine=myisam;
先修改再创建
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
....................
default-storage-engine=myisam
[root@localhost ~]# systemctl restart mysqld
查看引擎
查看所有引擎
mysql> show engines\G;
查看某一个表的引擎
mysql> show create table teacher \G;
三、MySQL事务
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。在数据库系统中,事务是工作的离散单位,它可以是修改一个用户的账户余额,也可以是库存项的写操作。在单用户、单数据库环境下执行事务比较简单,但在分布式环境下,维护多个数据库的完整性就比较复杂。大多数联机事务处理系统是在大型计算机上实现的,这是由于它的操作复杂,需要快速的输入/输出和完善的管理。如果一个事务在多个场地进行修改,那就需要管理机制来防止数据重写并提供同步。另外还需要具有返回失效事务的能力,提供安全保障和提供数据恢复能力。
【1】事务性质
(1)原子性(Atomicity)
事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。
(2)一致性(Consistency)
几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。
(3)隔离性(Isolation)
事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
(4)持久性(Durability)
对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
【2】事务作用
(1)为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
(2)当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
【3】数据库三大范式
数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
(1)第一范式:(确保每列保持原子性)
第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项(原子性),同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。
(2)第二范式:(确保表中的每列都和主键相关)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。
这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。
(3)第三范式:(确保每列都和主键列直接相关)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。
【4】事务的控制
MySQL事务默认是自动提交的,当SQL语句提交时事务便自动提交。
(1)事务开始
begin;
start transaction;
set autocommit=0;(禁止自动提交)
(2)提交事务
commit;
set autocommit=1;(开启自动提交)
rollback;
(3)回滚事务
rollback;
rollback to a;
(4)事务存档
设置存档点:savepoint a;
删除存档点:release savepoint;
【5】事务的创建
######修改存储引擎为innodb########
mysql> alter table teacher engine=innodb;
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
#######事务开始########
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
######禁止自动提交事务#########
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
######查看表信息##########
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 1 | zhangsan | nanjing |
+----+----------+----------+
5 rows in set (0.00 sec)
#########设置存档点###########
mysql> savepoint a;
Query OK, 0 rows affected (0.00 sec)
#######添加表信息#########
mysql> insert into teacher value(06,'yangyang','shanghai');
Query OK, 1 row affected (0.00 sec)
##########查询是否添加成功#########
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 6 | yangyang | shanghai |
| 1 | zhangsan | nanjing |
+----+----------+----------+
6 rows in set (0.00 sec)
#####回滚事务到存档点a################
mysql> rollback to a;
Query OK, 0 rows affected (0.00 sec)
#######再次查看表信息是否回滚##########
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 1 | zhangsan | nanjing |
+----+----------+----------+
5 rows in set (0.00 sec)
##########提交事务##########
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
【6】事务存档点问题解析
如果在一个存档点a的基础上新建另一个存档点b,若回滚到存档点a,则b存档点不存在。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> savepoint a;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into teacher value(06,'yang','lanzhou');
Query OK, 1 row affected (0.00 sec)
mysql> savepoint b;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 6 | yang | lanzhou |
| 1 | zhangsan | nanjing |
+----+----------+----------+
6 rows in set (0.00 sec)
mysql> insert into teacher value(07,'yang','lanzhou');
Query OK, 1 row affected (0.00 sec)
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 6 | yang | lanzhou |
| 7 | yang | lanzhou |
| 1 | zhangsan | nanjing |
+----+----------+----------+
7 rows in set (0.00 sec)
mysql> rollback to a;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from teacher;
+----+----------+----------+
| id | name | address |
+----+----------+----------+
| 5 | hanhan | shenzhen |
| 2 | lisi | nantong |
| 4 | wanger | hangzhou |
| 3 | wangwu | hangzhou |
| 1 | zhangsan | nanjing |
+----+----------+----------+
5 rows in set (0.00 sec)
mysql> rollback to b;
ERROR 1305 (42000): SAVEPOINT b does not exist