数据库
数据库(DataBase)简称DB。数据库是用于存储和管理数据的仓库。
数据库的特点:
常见的数据库排行榜
MySQL:开源免费的数据库,小型的数据库,已经被Oracle收购了。MySQL6.x版本也开始收费。后来Sun公司收购了MySQL,而Sun公司又被Oracle收购
Oracle:收费的大型数据库,Oracle 公司的产品。
DB2 :IBM 公司的数据库产品,收费的。常应用在银行系统中。
SQL Server:MicroSoft 公司收费的中型的数据库。C#、.net 等语言常使用。
SQLite: 嵌入式的小型数据库,应用在手机端,如:Android。
MySQL
MySQL服务启动:
1. 手动。
2. cmd--> services.msc 打开服务的窗口
3. 使用管理员打开cmd
net start mysql:启动mysql的服务
net stop mysql:关闭mysql服务
MySQL登录:
1. mysql -uroot -p密码
2. mysql -h + ip -uroot -p连接目标的密码
3. mysql --host=ip --user=root --password=连接目标的密码
MySQL退出:
1. exit
2. quit
MySQL目录结构
配置文件 my.ini
数据库、表、数据的关系图
数据库:文件夹
表:文件
数据:数据
SQL
SQL(Structured Query Language):结构化查询语言。它定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。
SQL通用语法
2. 可使用空格和缩进来增强语句的可读性。
3. MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
4. 拥有3种注释:
单行注释: -- 注释内容 或 # 注释内容(mysql 特有) (--中间必须要空格)
多行注释: /* 注释 */
SQL分类
1) DDL(Data Definition Language)数据定义语言
用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter等
2) DML(Data Manipulation Language)数据操作语言
用来对数据库中表的数据进行增删改。关键字:insert, delete, update等
3) DQL(Data Query Language)数据查询语言
用来查询数据库中表的记录(数据)。关键字:select, where 等
4) DCL(Data Control Language)数据控制语言(了解)
用来定义数据库的访问权限和安全级别,及创建用户。关键字:GRANT, REVOKE等
DDL:操作数据库、表
操作数据库:CRUD
1. C(Create):创建
* 创建数据库:
* create database数据库名称;
* 创建数据库,判断不存在,再创建:
* create database if not exists 数据库名称;
* 创建数据库,并指定字符集
* create database 数据库名称 character set 字符集名;
2. R(Retrieve):查询
* 查询所有数据库的名称:
* show databases;
* 查询某个数据库的字符集:查询某个数据库的创建语句
* show create database 数据库名称;
3. U(Update):修改
* 修改数据库的字符集
* alter database 数据库名称 character set 字符集名称;
在数据库中修改为utf-8字符集时不使用 - 而是utf8
4. D(Delete):删除
* 删除数据库
* drop database 数据库名称;
* 判断数据库存在,存在再删除
* drop database if exists 数据库名称;
5.使用数据库
* 查询当前正在使用的数据库名称
* select database();
* 使用数据库
* use 数据库名称;
操作表
1. C(Create):创建
create table 表名(
列名1 数据类型1,
列名2 数据类型2,
....
列名n 数据类型n
);
注意:最后一列,不需要加逗号(,)
数据库类型:
1. int:整数类型
age int,
2. double:小数类型
score double(5,2)
5表示此小数最多有五位,2表示小数点后最多保留两位
3. date:日期,只包含年月日,yyyy-MM-dd
4. datetime:日期,包含年月日时分秒yyyy-MM-dd HH:mm:ss
5. timestamp:时间戳类型,包含年月日时分秒yyyy-MM-dd HH:mm:ss
如果将来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值
6. varchar:字符串
name varchar(20):姓名最大20个字符
zhangsan 8个字符 张三 2个字符
如果超过最大数值则会报错
创建表:
create table student1(
id int,
name varchar(32),
age int ,
score double(4,1),
birthday date,
insert_time timestamp
);
复制表:
create table 表名 like 被复制的表名;
2. R(Retrieve):查询
查询某个数据库中所有的表名称
show tables;
查询表结构
desc 表名;
查询某个表的字符集;查询某个表的创建语句
show create table 表名;
3. U(Update):修改
1. 修改表名
alter table 表名 rename to 新的表名;
2. 修改表的字符集
alter table 表名 character set 字符集名称;
3. 添加一列
alter table 表名 add 列名 数据类型;
4. 修改列名称 类型
alter table 表名 change 列名 新列别 新数据类型;
alter table 表名 modify 列名 新数据类型;
5. 删除列
alter table 表名 drop 列名;
4. D(Delete):删除
drop table 表名;
drop table if exists 表名 ;
DML:增删改表中数据
1. 添加数据:
语法:
insert into表名(列名1,列名2,...列名n) values(值1,值2,...值n);
注意:
1. 列名和值要一一对应(个数和数据类型要相同)。
2. 如果表名后,不定义列名,则默认给所有列添加值
insert into 表名 values(值1,值2,...值n);
使用该方式一定要将所有列名赋值上对应的值,否则将会报错
3. 除了数字类型,其他类型需要使用引号(单双都可以)引起来
2. 删除数据:
语法:
delete from 表名 [where 条件]
注意:
1. 如果不加条件,则删除表中所有记录。
2. 如果要删除所有记录:
1. delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操作
2. TRUNCATE TABLE 表名; -- 推荐使用,效率更高:先删除表,然后再创建一张一样的空表。
只使用了两条代码(DROP TABLE + CREATE TABLE)
3. 修改数据:
语法:
update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];
注意:
DQL:查询表中的记录
1. 语法:
select
字段列表
from
表名列表
where
条件列表
group by
分组字段
having
分组之后的条件
order by
排序
limit
分页限定
2. 基础查询
1. 多个字段的查询
select 字段名1,字段名2... from 表名;
注意:
如果查询所有字段,则可以使用*来替代字段列表。
查询所有该表中的数据:select * from 表名;
但是使用*号查询列表的可读性很低,所以很多时候还是需要将全部字段名写出方便理解有哪些字段
2. 去除重复的结果集:
select distinct 字段名1,字段名2... from 表名;
去除重复必须保证结果集完全相同才会去除,只要有一条列表数据不同,就不会去除。
3. 计算列
一般可以使用四则运算计算一些列的值。(一般只会进行数值型的计算)
默认情况下null参与的运算,计算结果都为null。
ifnull(表达式1,表达式2):
表达式1:哪个字段需要判断是否为null
表达式2:如果该字段为null需要替换成的值
4. 起别名:
as:更改查询的字段名的名称,as也可以省略。
3. 条件查询
1. where子句后跟条件
2. 运算符(要在运算符前面写上需要操作的列名)
> 、< 、<= 、>= 、= 、<>
<>在SQL中表示不等于,在mysql中也可以使用!=,mysql中没有==
BETWEEN...AND...:表示查询在两个数值之间
IN(集合):表示查询该表中符合在括号集合中的任意一个数值的数据。
LIKE:模糊查询
占位符:
_:单个任意字符
可以使用指定数量的_占位符来表示需要指定几个字符的数据
%:多个任意字符
IS NULL:查询该列的数值是否有为null的(中间有空格)
null不能通过=(!=)来判断
and 或 &&
or 或 ||
not 或 !
在SQL语句中推荐使用英文
注意:在查询是可能遇到查询结果与预想不同的情况,可能是在添加数据时在某些数据后不小心添加上了一个空格导致的。
4.排序查询
语法:order by子句
order by 排序字段1 排序方式1,排序字段2 排序方式2...
排序方式(默认ASC):
ASC:升序,默认的。
DESC:降序。
注意:
如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件。
5. 聚合函数:将一列数据作为一个整体,进行纵向的计算。
1. count:计算个数
1. 一般选择非空的列:主键
2. count(*)
count(*)只要该行数据中有一列数据被赋值,就会计算该行
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值
注意:聚合函数的计算,排除null值。
解决方案:
1. 选择不包含非空的列进行计算
2. IFNULL函数
6.分组查询:
1. 语法:group by 分组字段;
2. 注意:
1. 分组之后查询的字段只能写:分组字段、聚合函数
2. where和having的区别?
1. where在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
2. where后不可以跟聚合函数,having可以进行聚合函数的判断。
7.分页查询
1. 语法:limit 开始的索引,每页查询的条数;
2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
3. limit是一个MySQL"方言"
MySQL数据类型
详细的数据类型如下
多表查询
多表查询的结果集也被称为笛卡尔积:
有两个集合A,B .取这两个集合的所有组成情况。
要完成多表查询,需要消除无用的数据
多表查询的分类:
1. 内连接查询:
1. 隐式内连接:使用where条件消除无用数据
例子:
-- 查询所有员工信息和对应的部门信息
SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
-- 查询员工表的名称,性别。部门表的名称
SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
-- 简化查询操作:起别名
SELECT
t1.name, -- 员工表的姓名
t1.gender,-- 员工表的性别
t2.name -- 部门表的名称
FROM
emp t1, -- 给表起别名
dept t2
WHERE
t1.`dept_id` = t2.`id`;
这样的多行写法是最正规的写法,将SQL语句写在一行内会非常影响阅读,当SQL语句内容繁多时。
2. 显式内连接
语法:select 字段列表 from 表名1 [inner] join 表名2 on 条件
例如:
SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;
SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`;
同样可以使用上例中的起别名操作等
3. 使用内连接查询的思维逻辑:
1. 从哪些表中查询数据
2. 条件是什么
3. 查询哪些字段
2. 外链接查询:
1. 左外连接
语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
查询的是左表所有数据以及其交集部分。
例子:
-- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
SELECT t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
2. 右外连接
语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
查询的是右表所有数据以及其交集部分。
例子:
SELECT * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
条件其实是用于筛选正确信息用的,而左外连接和右外连接查询的交集部分指的是两表的各条数据某数据是否被其他数据所使用,如果有则显示该条数据的所有内容
3. 子查询
概念:查询中嵌套查询,称嵌套查询为子查询。
-- 查询工资最高的员工信息
-- 1 查询最高的工资是多少 9000
SELECT MAX(salary) FROM emp;
-- 2 查询员工信息,并且工资等于9000的
SELECT * FROM emp WHERE emp.`salary` = 9000;
-- 一条sql就完成这个操作。子查询
SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);
子查询不同情况
1. 子查询的结果是单行单列的:
子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
-- 查询员工工资小于平均工资的人
SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
2. 子查询的结果是多行单列的:
子查询可以作为条件,使用运算符in来判断
-- 查询'财务部'和'市场部'所有的员工信息
SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
-- 子查询
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');
3. 子查询的结果是多行多列的:
子查询可以作为一张虚拟表参与查询
-- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
-- 子查询
SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
WHERE t1.id = t2.dept_id;
其实就是将SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11'作为一张表进行多表查询的隐式内连接操作
-- 普通内连接
SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` > '2011-11-11'
DCL
DBA:数据库管理员(职业)
DCL:管理用户,授权
1. 管理用户
1. 添加用户:
语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
2. 删除用户:
语法:DROP USER '用户名'@'主机名';
3. 修改用户密码:
UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';(不适用于mysql8)
mysql8使用: ALTER USER ‘用户名’@’主机名’ IDENTIFIED BY ‘新密码’
UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';
SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');
mysql中忘记了root用户的密码?
1. cmd -- > net stop mysql 停止mysql服务
需要管理员运行该cmd
2. 使用无验证方式启动mysql服务:mysqld --skip-grant-tables
3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就可以登录成功
4. use mysql;
5. update user set password = password('你的新密码') where user = 'root';
6. 关闭两个窗口
7. 打开任务管理器,手动结束mysqld.exe 的进程
8. 启动mysql服务
9. 使用新密码登录。
4. 查询用户:
-- 1. 切换到mysql数据库
USE myql;
-- 2. 查询user表
SELECT * FROM USER;
通配符:%表示可以在任意主机使用用户登录数据库(远程访问),包括本地主机
2. 权限管理:
1. 查询权限:
-- 查询权限
SHOW GRANTS FOR '用户名'@'主机名';
SHOW GRANTS FOR 'lisi'@'%';
2. 授予权限:
-- 授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
-- 给张三用户授予所有权限,在任意数据库任意表上
GRANT ALL ON *.* TO 'zhangsan'@'localhost';
3. 撤销权限:
-- 撤销权限:
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';
约束
概念:对表中的数据进行限定,保证数据的正确性、有效性和完整性。
分类:
1. 主键约束:primary key
2. 非空约束:not null
3. 唯一约束:unique
4. 外键约束:foreign key
非空约束:not null,值不能为null
1. 创建表时添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL
);
2. 创建表完后,添加非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;
3. 删除name的非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20);
唯一约束:unique,值不能重复
1. 创建表时,添加唯一约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(20) UNIQUE -- 添加了唯一约束
);
注意在mysql中,唯一约束限定的列的值可以有多个null
2. 删除唯一约束
ALTER TABLE stu DROP INDEX phone_number;
3. 在创建表后,添加唯一约束
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;
主键约束:primary key(两个关键字)
1. 注意:
1. 含义:非空且唯一
2. 一张表只能有一个字段为主键
3. 主键就是表中记录的唯一标识
2. 在创建表时,添加主键约束
create table stu(
id int primary key,-- 给id添加主键约束
name varchar(20)
);
3. 删除主键
ALTER TABLE stu DROP PRIMARY KEY;
一个列表中只能有一个主键,因此不需要在后面写上指定字段。
4. 创建完表后,添加主键
ALTER TABLE stu MODIFY id INT PRIMARY KEY;
5. 自动增长:
1. 概念:如果某一列是数值类型的,使用auto_increment可以来完成值得自动增长
2. 在创建表时,添加主键约束,并且完成主键自增长
create table stu(
id int primary key auto_increment,-- 给id添加主键约束
name varchar(20)
);
3. 删除自动增长
ALTER TABLE stu MODIFY id INT;
4. 添加自动增长
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
5. 注意:
1. 自动增长只会跟随上一条主键的数值之后,并不会根据主要的排序队伍走。
2. 一般情况下只有主键会使用自动增长
外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。
1. 在创建表时,可以添加外键
语法:
create table 表名(
....
外键列,
constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
);
外键名称:自定义名称,可随意起名但要注意不能重复。
2. 删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
3. 创建表之后,添加外键
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
在添加时可以省略ADD CONSTRAINT 外键名称,如果省略了,那么在创建该外键时,系统会自动给其添加一个唯一的名称。
4. 注意:外键的值可以为NULL,但不能为不存在的外键值
5. 级联操作
1. 分类:
1. 级联更新:ON UPDATE CASCADE
当绑定了的外键字段更改后,外键列也会随之一起更改。如果没有添加级联更新并且有与之关联的外键数据时,外键字段将无法修改。
2. 级联删除:ON DELETE CASCADE
当绑定了的外键字段删除后,外键列也会随之一起被删除。
2. 添加级联操作
语法:
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
数据库的设计
1. 多表之间的关系
1. 分类:
1. 一对一(了解):
* 如:人和身份证
* 分析:一个人只有一个身份证,一个身份证只能对应一个人
2. 一对多(多对一):
* 如:部门和员工
* 分析:一个部门有多个员工,一个员工只能对应一个部门
3. 多对多:
* 如:学生和课程
* 分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
2. 实现关系:
1. 一对多(多对一):
* 如:部门和员工
* 实现方式:在多的一方建立外键,指向一的一方的主键。
2. 多对多:
* 如:学生和课程
* 实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
3. 一对一(了解):
* 如:人和身份证
* 实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。
3. 案例
-- 创建旅游线路分类表 tab_category
-- cid 旅游线路分类主键,自动增长
-- cname 旅游线路分类名称非空,唯一,字符串 100
CREATE TABLE tab_category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100) NOT NULL UNIQUE
);
-- 创建旅游线路表 tab_route
/*
rid 旅游线路主键,自动增长
rname 旅游线路名称非空,唯一,字符串 100
price 价格
rdate 上架时间,日期类型
cid 外键,所属分类
*/
CREATE TABLE tab_route(
rid INT PRIMARY KEY AUTO_INCREMENT,
rname VARCHAR(100) NOT NULL UNIQUE,
price DOUBLE,
rdate DATE,
cid INT,
FOREIGN KEY (cid) REFERENCES tab_category(cid)
);
/*创建用户表 tab_user
uid 用户主键,自增长
username 用户名长度 100,唯一,非空
password 密码长度 30,非空
name 真实姓名长度 100
birthday 生日
sex 性别,定长字符串 1
telephone 手机号,字符串 11
email 邮箱,字符串长度 100
*/
CREATE TABLE tab_user (
uid INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) UNIQUE NOT NULL,
PASSWORD VARCHAR(30) NOT NULL,
NAME VARCHAR(100),
birthday DATE,
sex CHAR(1) DEFAULT '男',
telephone VARCHAR(11),
email VARCHAR(100)
);
/*
创建收藏表 tab_favorite
rid 旅游线路 id,外键
date 收藏时间
uid 用户 id,外键
rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
*/
CREATE TABLE tab_favorite (
rid INT, -- 线路id
DATE DATETIME,
uid INT, -- 用户id
-- 创建复合主键
PRIMARY KEY(rid,uid), -- 联合主键
FOREIGN KEY (rid) REFERENCES tab_route(rid),
FOREIGN KEY(uid) REFERENCES tab_user(uid)
);
2. 数据库设计的范式
概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
一般设计的数据库满足了前三个范式使用起来就比较合理了
分类:
1. 第一范式(1NF):每一列都是不可分割的原子数据项
即表中的某个列有多个值时,必须拆分为不同的列。简而言之,第一范式每一列不可再拆分,称为原子性。
第一范式存在的问题:
2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于(在1NF基础上消除非主属性对主码的部分函数依赖)
解决了数据冗余的问题:将部分函数依赖外的属性去除或创建另一个需要的表将其余的属性完成第二范式规则
第二范式的特点:
1)
一张表只描述一件事情。
2)
表中的每一列都完全依赖于主键
几个概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号-->姓名。 (学号,课程名称) --> 分数
2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值的确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值的确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) -- > 姓名
4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号-->系名,系名-->系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
主属性:码属性组中的所有属性
非主属性:除过码属性组的属性
3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于属性(在2NF基础上消除传递依赖)
解决了数据添加删除的问题:将传递函数依赖的属性添加到另一个表中,使属性之间不再存在依赖。
数据库的备份还原
1. 命令行:
语法:
备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
mysqldump -u root -p root db1 > D:\java学习资料
还原:
1. 登录数据库
2. 创建数据库
3. 使用数据库
4. 执行文件:source文件路径
2. 图形化工具:
事务
1. 事务的基本介绍
1. 概念:
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
开启事务后,事务内所包含的代码操作的数据将会变为临时数据,在确认数据是否正确后使用回滚(rollback)或提交(commit)才会将其修改。
原理图
2. 操作:
1. 开启事务: start transaction;
2. 回滚:rollback;
在哪开启的事务,就会在将数据返回至开启之前。
3. 提交:commit;
3. 例子:
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);
SELECT * FROM account;
UPDATE account SET balance = 1000;
-- 张三给李四转账 500 元
-- 0. 开启事务
START TRANSACTION;
-- 1. 张三账户 -500
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
-- 2. 李四账户 +500
-- 出错了...
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
-- 发现执行没有问题,提交事务
COMMIT;
-- 发现出问题了,回滚事务
ROLLBACK;
4. MySQL数据库中事务默认自动提交
事务提交的两种方式:
自动提交:
mysql就是自动提交的
一条DML(增删改)语句会自动提交一次事务。
手动提交:
Oracle数据库默认是手动提交事务
在手动提交下,执行的DML语句同样需要使用commit关键字来提交数据,数据才会发生变化。
需要先开启事务,再提交
修改事务的默认提交方式:
查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交0代表手动提交
修改默认提交方式: set @@autocommit = 0;
2. 事务的四大特征:
1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3. 隔离性:多个事务之间。相互独立。
4. 一致性:事务操作前后,数据总量不变
3. 事务的隔离级别(了解)
概念:多个事务之间隔离的,相互独立的。但是如果多个事务(并发)操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
存在问题:
1. 脏读:一个事务,读取到另一个事务中没有提交的数据
2. 不可重复读(虚读):一个事务中两次读取的数据内容不一致,要求的是一个事务中多次读取时数据是一致的,这是事务update时引发的问题
3. 一个事务中两次读取的数据的数量不一致,要求在一个事务多次读取的数据的数量是一致的,这是insert或delete时引发的问题
隔离级别:
1. read uncommitted:读未提交
产生的问题:脏读、不可重复读、幻读
2. read committed:读已提交 (Oracle)
产生的问题:不可重复读、幻读
3. repeatable read:可重复读 (MySQL默认)
产生的问题:幻读
4. serializable:串行化
可以解决所有的问题
如果一个事务在操作数据并且没有提交或回滚数据,那么在其他地方就无法对相同数据进行操作(类似于多线程的同步锁,另一个线程在输入代码后会等待拿到锁的(在事务中的操作)线程结束后(提交/回滚)才会执行该代码(BLOCKED状态))
注意:隔离级别从小到大安全性越来越高,但是效率越来越低
数据库查询隔离级别:
* select @@tx_isolation;
数据库设置隔离级别:
set global transaction isolation level 级别字符串;
演示:
set global transaction isolation level read uncommitted;
start transaction;
-- 转账操作
update account set balance = balance - 500 where id = 1;
update account set balance = balance + 500 where id = 2;
5. 回滚点
在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都已经成功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之为回滚点。
回滚点的操作语句
设置回滚点 savepoint 名字
回到回滚点 rollback to 名字
函数:在数据库中都有函数,这些函数属于系统函。除此之外用户也可以编写用户自定义函数。用户定义函数是存储在数据库中的代码块,可以把值返回到调用程序。调用时如同系统函数一样,如max(value)函数,其value被称为参数。函数一般功能比较简单,对于mysql函数只有传入参数,不像存储过程一样,有输入输出参数。
数据库函数特点如下:
· 存储函数将向调用者返回一个且仅返回一个结果值。
· 存储函数嵌入在sql中使用的,可以在select中调用,就像内建函数一样,比如cos()、hex()。
· 存储函数的参数类型类似于IN参数。
自定义函数
自定义函数简单语法:
CREATE FUNCTION 函数名称(
变量名称 类型,
变量名称 类型
)
RETURN 类型
BEGIN
DECLARE... 定义变量;
-- 声明, 语句要完成的操作,
RETURN 变量;
END;
Mysql索引
·官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。
·一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。
·我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说明,默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引。
索引的优势和劣势
优势:
·可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。
·通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
*被索引的列会自动进行排序,包括【单列索引】和【组合索引】,只是组合索引的排序要复杂一些。
*如果按照索引列的顺序进行排序,对应order by语句来说,效率就会提高很多。
劣势:
·索引会占据磁盘空间
·索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不仅要保存数据,还有保存或者更新对应的索引文件。
索引类型
·主键索引
索引列中的值必须是唯一的,不允许有空值。
·普通索引
MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。
·唯一索引
索引列中的值必须是唯一的,但是允许为空值。
·全文索引
只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。 MyISAM和InnoDB中都可以使用全文索引。
·空间索引
MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。
·前缀索引
在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。
·其他(按照索引列数量分类)
1.单列索引
2.组合索引
组合索引的使用,需要遵循最左前缀匹配原则(最左匹配原则)。一般情况下在条件允许的情况下使用组合索引替代多个单列索引使用。
索引的数据结构
MySQL的索引采用了B+树的数据结构。
Mysql的索引实现
Mysql有两种存储引擎的索引实现:MyISAM索引和InnoDB索引
MyIsam索引
以一个简单的user表为例。user表存在两个索引,id列为主键索引,age列为普通索引:
CREATE TABLE `user`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_age` (`age`) USING BTREE
) ENGINE = MyISAM
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
MyISAM的数据文件和索引文件是分开存储的。MyISAM使用B+树构建索引树时,叶子节点中存储的键值为索引列的值,数据为索引所在行的磁盘地址。
在 MyISAM 中,辅助索引和主键索引的结构是一样的,没有任何区别,叶子节点的数据存储的都是行记录的磁盘地址。只是主键索引的键值是唯一的,而辅助索引的键值可以重复。
查询数据时,由于辅助索引的键值不唯一,可能存在多个拥有相同的记录,所以即使是等值查询,也需要按照范围查询的方式在辅助索引树中检索数据。
主键索引
根据主键等值查询数据:
select * from user where id = 28;
磁盘IO次数:3次索引检索+记录数据检索。
根据主键范围查询数据:
select * from user where id between 28 and 47;
磁盘IO次数:4次索引检索+记录数据检索。
备注:以上分析仅供参考,MyISAM在查询时,会将索引节点缓存在MySQL缓存中,而数据缓存依赖于操作系统自身的缓存,所以并不是每次都是走的磁盘,这里只是为了分析索引的使用过程。
InnoDB索引
主键索引(聚簇索引)
每个InnoDB表都有一个聚簇索引 ,聚簇索引使用B+树构建,叶子节点存储的数据是整行记录。一般情况下,聚簇索引等同于主键索引,当一个表没有创建主键索引时,InnoDB会自动创建一个ROWID字段来构建聚簇索引。 InnoDB创建索引的具体规则如下: //@TODO
Mysql函数
group_concat
·DISTINCT:去除重复数据
SUBSTRING_INDEX
用于截取目标字符串
substring_index(string,sep,num):
·string:用于截取目标字符串的字符串。可为字段,表达式等。
·sep:分隔符,string存在且用于分割的字符,比如“,”、“.”等。
·num:序号,为非0整数。若为整数则表示从左到右数,若为负数则从右到左数。比如“www.mysql.com”截取字符‘www’,分割符为“.”,从左到右序号为1,即substring_index("www.mysql.com",'.',1);若从右开始获取“com”则为序号为-1即substring_index("www.mysql.com",'.',-1)
date_add