1.MySQL数据库概述
1.1基本概念
数据(Data)
数字、字母、图片、视频、音频等
元数据(MetaData)
数据的数据
数据库(DataBase)
存储数据的仓库
数据库管理系统(DBMS)
用来管理数据/数据库的一个系统(软件),保证数据库的安全性、一致性、完整性。
SQL语言
结构化的查询语言,是用来操作关系型数据的标准语言
1.2常用命令:
show databases;
show tables;
desc 表名 -查看表结构
use 某个数据库 -使用哪个数据库
select database(); 查看正在使用哪个数据库
creata database xxx 创建数据库;
drop database xx 删除某个数据库
create table xx 创建表
drop table xx 删除表
1.3语句分类
DDL语句 (数据定义语句):针对数据库对象的操作
create、 drop、alter
DML语句(数据操作语句):对数据做增删改查操作,默认都是对表数据的操作
insert、update、delete
查询语句
select
DCL语句(数据控制语句):对用户授权及删除权限
grant、revoke
事务控制语句
commit、rollback、savepoint
1.4SQL语句写法:
1. SQL语句的标准语法中没有分号,在mysql的命令行中使用分号确定一条sql语句的结束。
2. SQL语句不区分大小写,包括:关键字、表名、列名,但是数据区分大小写
3. SQL语句虽然是标准的关系型数据库操作语句,但是在不同的数据库系统中SQL语句略有不同
2.MySQL初级
2.1MySQL数据类型
-
数值类型
整数类型:
int 类型:标准的整数 4字节
浮点类型:
float类型:单精度 4字节 限定整数及小数的位数: float (M,N) M位数字,N位小数
double类型:双精度 8字节 限定整数及小数的位数
decimal类型:长度和限定的位数相关的
-
字符串类型
char类型:定长字符串,字符串的长度一定是限制的长度。读写速度快,但占空间
varchar类型:变长字符串 ,读写速度比char慢,节省空间 最大长度 65535
text类型:文本串
blob类型:二进制 (图片、音频、视频)
-
日期时间类型
date类型:年月日
time类型: 时分秒
datetime类型:年月日时分秒
2.2create语句
创建表的语法:
create table 表名 (
字段名 数据类型 [其他设置]
[ , 字段名 数据类型 [其他设置] ]
)[字符集和表引擎]
表名用英文,尽量表述其存储数据的含义
列的属性修饰:
-
自增长属性:如果为列设置了自增长属性,这列的插入数据为空时,列的值自动+1,内部维护了一个序号生成器,自增长属性的列一定是主键,类型一定是整数
-
默认值 :在定义列的后面 : default 值 ,为这列添加了默认值
workyear int default 0
-
二进制存储: 区分大小写,mysql在存储varchar时默认是不区分大写的 binary
2.3约束
约束数据,当存储的数据违反了约束,不能存储,为了保证数据的完整性、一致性
2.3.1. 主键约束
主键的特点是唯一且非空(没有自增长),通过主键来确定唯一的一条记录
想要在表创建之后删除一个有自增长属性的主键 必须先要去掉它的自增长属性
每个表都要有主键约束。
create table student(
-- 存储的id 与业务无关,是程序员用的 1~9999999
sid int primary key,
sno int ,
sheight float(3,2),
sname varchar(3) ,
sex char(1),
age int,
tel varchar(11),
birthday date
)
create table student(
-- 存储的id 与业务无关,是程序员用的 1~9999999
sid int ,
sno int ,
sheight float(3,2),
sname varchar(3) ,
sex char(1),
age int,
tel varchar(11),
birthday date,
constraint p_student primary key (sid)
)
推荐使用第二种写法
提示:Duplicate entry ‘X’ for key PRIMARY
2. 3.2外键约束
外键是用来关联表和表的关系的,一个表的外键要关联到另一个表的主键(一个表的外键值存在于另一个表的主键中), 外键只能关联唯一性索引 可以是另一个表的主键或者是有唯一性约束的列保证数据的正确性;外键允许为null
-- 部门表 deptno是主键
create table dept(
deptno int ,
deptname varchar(16),
constraint pk_dept primary key(deptno)
)
-- 员工表 编号(主键)、姓名、部门编号(外键)
create table emp(
empno int ,
ename varchar(16),
deptno int,
constraint pk_emp primary key(empno),
constraint fk_emp_dept FOREIGN key(deptno)
references dept(deptno)
)
思考问题:
1. 一个表可以有一个主键约束,可以有多个外键约束
2. 向两张表插入数据时,两个数据有关系,先插入主键表,再插入外键表
3. 删除数据时,先删除外键表,再删除主键表。
2.3.3.唯一约束
约束的列可以是一列也可以是多列,约束的列的值具有唯一且允许为空的特性
通常有些数据在业务上是唯一的,但不是主键,例如电话号、订单号、订单号+序号组合
create table orders (
orderno varchar(12),
customerid int,
tel varchar(11),
constraint pk_orders primary key(orderno),
constraint unique_orders_tel unique(tel)
)
create table ordersdetail(
detailid int ,
orderno varchar(12),
seq int,
item varchar(12),
constraint pk_orderdetail primary key(detailid),
constraint fk_orderdetail_orderno foreign key (orderno)
references orders(orderno),
constraint unique_ordersdetail_noseq unique(orderno,seq)
)
2.3.4. 非空约束
约束某一列是否允许为空值,前面的约束都是表级约束,非空约束是列级约束
create table orders (
orderno varchar(12),
customerid int,
tel varchar(11) not null,
constraint pk_orders primary key(orderno),
constraint unique_orders_tel unique(tel)
)
###5. 检查约束
约束的列的值符合检查的条件,mysql默认是不支持检查约束
2.4alter语句
更改数据库对象结构,例如表结构、视图、索引等
语法:
alter table 表名 action
action: 不是关键字 有一些动作组成的,包括add 、 modify/change、 drop 、rename
主键才能有自增长 主键不能有null 唯一约束可以有null
例如:
-
在locations表中 新建一个字段country_id
alter table locations add country_id char(2);
-
在locations表中删除一个字段
alter table locations drop test_id;
-
在locations表中 country_id 的类型改为varchar(2)
alter table locations change country_id country_id varchar(2);
4.在locations表中追加id(int)的自增长属性
alter table locations change id id int auto_increment;
5.在locations表中删除id的自增长属性
alter table locations change id id int;
-
将testa表 改名为 testb表
alter table testa rename to testb;
-
为 locations表增加主键约束 location_id列
alter table locations add constraint pk_locations primary key(location_id)
-
删除locations的主键约束
alter table locations drop primary key;
-
删除student表的cno的外键约束
alter table student drop foreign key fk_student_classno;
#创建表 两张 有关联关系 只添加非空 自增长约束
#添加一个字段 改变某一列数据类型 删除一个字段 重命名表
#删除非空(主键) 自增长约束(主键)
#添加主键约束 外键约束并进行关联 唯一约束(非主键)
#再次添加外键约束 关联到唯一约束上
# 主键特点:非空 唯一
#1.创建a表
create table a(
aid int auto_increment not null,
aname varchar(12) ,
asex char(1),
aclass_no int,
atest int,
auni_test DOUBLE(3,2),
CONSTRAINT uni_ani_test UNwshIQUE(auni_test),
CONSTRAINT pk_aid PRIMARY key(aid)
)
#2创建b表
create table b(
bid int ,
bcno varchar(12),
buni_test DOUBLE(3,2)
)
#3.为b表加上主键约束
alter table b add CONSTRAINT pk_bid PRIMARY KEY (bid);
#4.删除a表中的atest 后加上
alter table a DROP atest;
alter table a add atest int;
#5.修改a表atest的属性 改为varchar 修改b表的bcno类型为int
alter table a change atest atest VARCHAR(12);
alter table b change bcno bcno int;
#6.为b表的主键bid加上自增长属性 后删除 再加上
alter table b change bid bid int auto_increment;
alter table b change bid bid int;
#7.为b表的buni_test加上唯一约束 后删除
alter table b add constraint uni_buni_test UNIQUE(buni_test);
-删除一个唯一约束 要使用drop index的方式 与删除主键和外键不同 (drop Primary key xx)
alter table b drop index uni_buni_test;
#8为a表的auni_test加上唯一约束
alter table a add constraint uni_auni_test UNIQUE(auni_test)
#9.设置b表的buni_test为外键 并关联到a表的auni_tset(说明外键也可以关联另一张表的唯一约束键上)
alter table b add CONSTRAINT fk_buni_test FOREIGN key(buni_test) REFERENCES a(auni_test)
#10.为a表的aclassno加上自增长
-- 下面语句报错 there can be only one auto column and it must be defined as a key 说明自增长只能设置给一个键 (主键)
x alter table a change aclass_no aclass_no int auto_increment
#11.给b表的外键buni_test 加自增长 外键不能加自增长
x alter table b change buni_test buni_test int auto_increment;
#12 给a表新增tt 并设置唯一 添加自增长
alter table a add tt int;
alter table a add constraint uni_tt UNIQUE(tt);
唯一性约束的字段也无法加自增长 只有主键能加自增长
x alter table a change tt tt int auto_increment;
#13给b表加入新列 qq 设置默认值
增加默认值
alter table b add qq int default 1
#14.改变b表中的qq字段的默认值 这个使用alter
修改默认值
alter table b alter qq set DEFAULT 2
删除默认值
alter table b alter qq drop DEFAULT
#15.改变b表的表名为 c 再改回来
alter table b rename to c
alter table c rename to b
2.5drop语句
删除表结构,表数据也移除
drop table if exists testb; -- 保证语句能成功执行
2.6insert语句
是DML语句 语法中都是对 table的默认操作
语法:
insert into 表名 [(字段名, 字段名…)]
values (值, 值…)
解释:插入的值 和 表名后面指定的字段 顺序要对应 ,向表中插入一条记录
如果表名的后面 没有写字段名,字段的顺序按照建表时字段的顺序排列
insert into locations
values(1009,'北京市海淀区2',100043,'北京','北京',86)
insert into locations(street_address,postal_code)
values('北京市朝阳区2',100046)
insert into locations(location_id,street_address)
values(default,'北京市海淀区2')
-- mysql支持
insert into locations(street_address,postal_code)
values('北京市朝阳区5',100046),
('北京市朝阳区6',100046),
('北京市朝阳区7',100046)
可能出现的错误:
-
指定的列名不存在
Unknown column ‘xxxx’ in ‘field list’
- 列数量和值数量 不一致
Column count doesn’t match value count at row 1
2.7select语句
查询语句可以查询部分列内容 ,查询部分数据行内容,表和表的联合查询
2.7.1简单查询
语法:
select * from 表名
或
select 字段名,字段名… from 表名
其中 * 代表所有列 ,在项目开发时不建议写 * ,因为效率慢
select 和from 之间可以写:
-
别名
检索出的记录是结果集 ,对结果集的列起的别名
select first_name fn(fn就是别名),phone_number tel from employee
-
运算表达式
每个员工的年薪 全文检索 没有加条件查询 select first_name,salary*12 from employee
-
distinct 关键字 去重复
检索员工表的部门和岗位 select distinct department_id,job_id from employee select distinct department_id from employee
-
使用函数
直接通过函数名调用 ,每个函数一定有结果
参照:资料:《MySQL常用函数》
2.7.2条件查询
查询出表中的部分行数据
语法:
select * from 表名 where boolean_expression
检索条件包括:
-
比较条件 : = > < >= <= <>
- 逻辑运算 :and or not 注意:and的优先级高于or
select * from employee where first_name <> 'jack' or salary > 5000 select * from employee where first_name <> 'jack' select * from employee where not first_name = 'jack'
- 区间范围条件:between … and …
select * FROM employee where salary >=5000 and salary <=10000 select * from employee where salary between 5000 and 10000
- 匹配查询条件/模糊查询: like关键字和两个符号组合使用,分别是%和_
其中% 代表0或n个任意字符 , _代表单个任意字符
-- 查询姓名以t开头的 select * from employee where binary first_name like 't%' -- 查询姓名第二个字母是o的员工 select * from employee where first_name like '_o%' -- 查询姓名第二个字母不是o的员工 select * from employee where first_name not like '_o%'
- 包含查询条件:in关键字 (内容)
-- 查询出姓名是tom jim rose jack 的员工 select * from employee where first_name in ('tom','jim','rose','jack') -- 查询出姓名不是tom jim rose jack 的员工 select * from employee where first_name not in ('tom','jim','rose','jack')
- null值的查询: 在sql中null值和空字符串是不同的值 ,在null值检索时要使用 is null或is not null
-- 没有部门的员工 select * from employee where department_id is null -- 有部门的员工 select * from employee where department_id is not null
--查询时如果要区分大小写 就在where 后 加 binary 查询区分大小写的以t开头的人 select * from employee where BINARY first_name like 't%'
2.7.3排序查询
对查询的结果集进行排序,使用 order by 字段名 [,字段名] [asc](默认的升序) [desc](降序)修饰的是当个字段
select * from 表名 [where …] order by 字段名 [,字段名] [asc](默认的升序) [desc](降序)
-- 查询出所有员工 按工资排序
select first_name,salary from employee order by salary
-- 查询出所有员工 按部门升序、工资降序排序
select first_name,department_id,salary from employee
order by department_id,salary desc
思考:order by 后面使用别名? where的后面使用别名?
select first_name fn,salary s from employee
where salary > 5000
order by s
别名是查询结果出来之后 为结果集中的字段起别名
order by 后面可以使用别名 where不能
order by 是排序查询出来的结果集中的内容 这是的结果集已经是别名了
where 的时候还没有结果集 所有不能
2.8分组函数/聚合函数
sum() 总和
avg() 平均
max() 最大
min() 最小
count() 记录数
select sum(salary),avg(salary),max(salary),min(salary),count(salary) from employee
分组函数和null值的使用,分组函数运算不包括 null 值
ifnull (m ,n) 如果m是null 结果是n 如果m不是null 结果是m
分组语句
可以对查询结果进行分组,配合分组函数可以进行一些统计分析的查询
select * from 表名 where … group by 字段名,字段名 order by …
查询各个部门的平均工资
select avg(salary) from employee group by department_id
注意事项:
1. 分组函数和字段一同查询时,字段一定要在**group by**语句中
2. 当分组函数作为查询条件时,where语句中不能包含分组函数,使用having完成分组函数的条件限定
3. where一定要放在group by 前面
查询工资大于4000的平均工资大于5000的部门
select department_id,avg(salary)
from employee
where salary > 4000
group by department_id
having avg(salary) > 5000
order by department_id
过程:查询出工资大于4000的员工 相当是(select * from employee where salary > 4000)形成结果集
对结果集数据进行分组,在结果集中限定having的条件,排序显示
- mysql的分组函数不能嵌套
- count (一个或一些字段) 只能写一个 或者 * 或者数字 获得字段不为null的记录数
- (1)sum()函数里面的参数是列名的时候,是计算列名的值的相加,而不是有值项的总数。
(2)sum(条件表达式),如果记录满足条件表达式就加1,统计满足条件的行数 - (1)COUNT()函数里面的参数是列名的的时候,那么会计算有值项的次数。(NULL 不计入, 但是’'值计入)
(2)COUNT(*)可以计算出行数,包括null
(3)COUNT(1)也可以计算出行数,1在这里代表一行
(4)COUNT(column)对特定的列的值具有的行数进行计算,不包含NULL值
(5)COUNT(条件表达式),不管记录是否满足条件表达式,只要非NULL就加1
有多少个员工
select count(employee_id) from employee
select count(department_id) from employee
select count(1) from employee
select count(null) from employee 结果是0 不统计null
select count(*) from employee
select 1 from employee
计算表的记录数
select count(*) from employee
select count(1) from employee
select count(employee_id) from employee
2.9多表连接查询
查询的数据或条件分布在多张表中,通过多表连接查询实现。
例如:查询员工姓名(employee)和部门名称(departments),
查询工资等级是2级(job_grades)的员工姓名(employee)
2.9.1等值连接和内连接
等值连接:
两个表的等值连接时,会将两个表行数据的所有组合查询到结果集中,乘积叫笛卡尔积,但是这样的组合并不正确,通过where语句过滤出正确的组合 给表起别名
select * from employee e,departments d
where e.department_id = d.department_id
不等值连接
查询员工姓名和工资等级
select first_name,grade_level
from employee e,job_grades g
where salary >= lowest_sal and salary < highest_sal
缺点:只能查询出符合where条件的数据
等值连接的结果和内连接的结果相同
内连接:
查询财务部员工姓名和部门名称 结果和等值连接相同
select * from employee e inner join departments d
on e.department_id = d.department_id
and department_name = '财务部'
select * from employee e inner join departments d
on e.department_id = d.department_id
where department_name = '财务部'
on不是查询条件 而是 连接条件
2.9.2左外连接和右外连接
查询出左表的所有数据
A 左外连接 B 那么 A表就是 左表
结果显示A表的所有数据
A 右外连接 B 那么 B表就是 左表
结果显示B表的所有数据
查询出所有的员工姓名和部门名称
select * from employee e left outer join departments d
on e.department_id = d.department_id
查询出所有员工的姓名、部门名称、工作地点
select * from employee e
left outer join departments d
on e.department_id = d.department_id
left outer join locations loc
on d.location_id = loc.location_id
where ...
group by ....
having ...
order by ...
2.9.3自连接
表自己连接自己,可以左外连接的自连接、内连接的自连接
查询员工姓名和员工管理者姓名
select e.first_name,e.salary,m.first_name,m.salary from employee e
left outer join employee m on e.manager_id = m.employee_id
查询员工姓名部门名称所在地、其管理者姓名部门名称所在地
select e.first_name,d.department_name,loc.city,m.first_name,md.department_name,mloc.city
from employee e
left outer join employee m on e.manager_id = m.employee_id
left outer join departments d on e.department_id = d.department_id
left outer join departments md on m.department_id = md.department_id
left outer join locations loc on d.location_id = loc.location_id
left outer join locations mloc on md.location_id = mloc.location_id
2.10子查询
包含在一个查询语句中的另一个查询
select * from employee
where job_id = (select job_id from employee where first_name = 'jack')
-
几乎所有的查询需求都能用子查询解决,效率低,原因是每检索外查询的一条记录执行where中的子查询,例如上面的语句总共会检索 12*12次
-
根据子查询的结果 分为单行子查询和多行子查询 ,在where语句中 单行子查询只能用 = > < <> 限定条件 ,多行子查询可以用 any all in exist限定
-
any : 任意一个,哪个都行 ; all 全部都符
select first_name,salary from employee where salary > any(select salary from employee where department_id = 5001) select first_name,salary from employee where salary > all(select salary from employee where department_id = 5001)
-
exist修饰子查询 结果是true/false ,如果exist修饰的子查询有记录则结果是true,没有记录结果是false,exist修饰子查询的效率 比 in修饰子查询的效率高
select * from employee where department_id in (select department_id from departments) 效率高,在exists的子查询执行时,如果找到了记录就立刻返回true select * from employee e where exists (select 1 from departments d where e.department_id = d.department_id); select * from departments;
转换方式
例如:select * from A where aid in (select bid from B)
select * from A a where exists(select 1 from B b where a.aid= b.bid)
-
子查询不仅可以写在where中,也可以写在from的后面,对子查询的结果集再次进行查询
# 查询出部门平均工资最高工资 select max(a.avgsal) from (select avg(salary) avgsal from employee group by department_id) a
-
union和unionall 修饰子查询 union联合
可以将多个子查询的结果合并
行合并
- 合并的两个结果集 一定要有相同数量的列,(对应顺序的列类型相同oracle数据库)。
- union 去掉重复的数据合并 union all 不去重合并。
- 应用场景:报表查询中结果集的联合查询或聚合操作、列转行操作。
2.11分页查询
限定从哪行开始查询,查询多少行。写在查询SQL语句的最后
查询员工表的前5条记录 (应用程序界面:第一页 每页显示5条)
select * from employee order by first_name limit 0,5
查询员工表的前6~10条记录 (第二页 每页显示5条)
select * from employee order by first_name limit 5,5
查询员工表的前11~15条记录 (第三页 每页显示5条)
select * from employee order by first_name limit 10,5
假设 java变量 pageNum当前页数 pageSize 每页记录数
select * from employee limit (pageNum-1)*pageSize,pageSize
2.12update语句
更新数据语句
语法:
update 表名 set 字段名=值 [, 字段名=值] [where …]
update employee set salary = 26000,job_id='财务总监' where employee_id = 103
在mysql中 update后面的表可以写多表连接
# 将北京的员工工资加一千 子查询
update employee set salary = salary + 1000 where employee_id
in (select employee_id from employee e
left outer join departments d on e.department_id = d.department_id
left outer join locations loc on d.location_id = loc.location_id
where city = '北京')
# update 后面可以接左外连接
update employee e
left outer join departments d on e.department_id = d.department_id
left outer join locations loc on d.location_id = loc.location_id
set salary=salary+1000 where loc.city='北京'
2.13delete语句
删除数据的,可以按照条件删除
语法:
delete from 表名 [where …]
delete和truncate和drop的区别?
delete是 DML语句, 删除数据
drop是 DDL语句,删除表结构和所有数据
truncate是清空表数据操作,删除的速度比delete快,但是不能按条件删除,不是事务回滚。
truncate table score
2.14case when 语法
第一种
case '字段名' when 值 then xx
when 值 then xx
。。。
else 。。
end
第二种
case when '条件表达式' then xx
when '条件表达式' then xx
else xx end
3.事务
1.事务的特性ACID
MySQL默认是自动提交事务 自动将 每一条SQL语句 都自动封装在一个事务中 自动提交
通过连接来控制事务
-- 查看数据库的当前连接自动提交是够开启
show variables like 'autocommit';
begin Transaction(默认是自动的开启事务)
一个SQL语句默认是一个事务
update xxx ....
commit (自动提交的)
---------------------------------------------------
show variables like 'autocommit'; -- 查看当前连接的是否开启自动提交
set autocommit = 0; 取消当前连接的自动提交事务 变为手动提交
如何手动开始事务
1. start transaction 或者 begin 开始事务
2. 写语句 {
delete xx ...
savepoint a ; -- 设置保存点
update xx ...
savepoint b;
rollback to a/b; -- 可选回滚到哪个状态 回滚不是失败
}
3. commit; 提交事务
每个事务都有的特性
•原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包括的诸操作要么都做,要么都不做 即要么都成功 要么 都失败(rollback是取消 不是失败 )
•一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致状态。
比如 两个账户 A B A原来有500 B有500 这时A B一共1000 现在A给B转100 转账完成后保证 A和B 总的还是1000 就是从一个一致性到另一个一致性
•隔离性(Isolation):一个事务的执行不能被其它事务干扰,即一个事务内部操作及使用的数据对其它并发是隔离的,并发执行的各个事务之间不能互相干扰。
--- 在事务的并发操作同一个数据时 事务的隔离级别
--- 事务并发产生的三大问题
-- 查看数据库的隔离级别 select @@tx_isolation
-- 四种隔离
1. Read uncommited 允许事务读取未被其他事务提交的数据 (脏读 不可重复读 幻读都会发生)
2. Read committed 只允许事务读取已经被其他事物提交的数据 --避免脏读 (不可避免不可重复读 和幻读
3. Repeatable Read 确保可以从一个事务中读取到相同得值 本质就是 --行锁-- 不可避免幻读
4. Serializable 本质是表锁 避免一切问题 性能十分低下
-- 设置当前连接隔离级别 只是在当前连接中生效 重启后就无效了 想要持久化 在mysql的配置文件中设置
set transaction isolation level xxx
-- 设置数据库系统的全局的隔离级别
set global transaction isolation level xxx
--------------------------------------------------------------------------------------
1. 脏读
A 和 B 两个事务 A读到了B未提交的数据 B如果回滚 那么A读到的就是临时且无效的数据
-- 数据库隔离级别(Read uncommitted)
解决办法 设置隔离级别为 (Read committed)
set session transaction isolation level read committed
-- Read uncommitted隔离级别 当A事务查询一条数据时 B事务执行了更新语句 A再次查询发现数据变了
这时B回滚了 A再次查询 发现数据又变了 当A进行写时 为这行数据加锁其他事物只能读不能写
-- Read Committed隔离级别 只能读取别的事务提交后的数据(在一个事务中读取到的数据也可能不同,读取了其他事物提交后的数据) 写的时候也加上行锁
--------------------------------------------------------------------------------------
2. 不可重复读(类似多线程并发问题)
A 和 B 两个事务 A先读一个数据为1 这是B更改了这个数据为2 并且提交了 A再次读的时候这个数据变成了2
这个跟脏读很像 但是脏读的发生只是 B事务更改数据后没有提交进行了回滚 但是不可重复读B事务进行了提交
A 和 B 事务是并发执行的 A读取到的数据就有可能不一致 解决这个问题就是--(在开启事务的时候读到的数据到事务提交前不会改变 不管别的事务是否提交,写的时候加锁(行锁))
-- 解决办法 设置隔离界别为 Repeatable Read
set session transaction isolation level Pepeatable Read (mysql默认的隔离级别)
本质就是加了行锁 所有事务(可以共同读 (读取的始终是一个值 但是更新删除必须等待其他的事务提交)
: 三个事务 A B C
-- A()
{
1. start transacation; -- 开启事务
2. select dataK -- 查询了一行数据 显示为dataK
5. select dataK -- 再次读读出的还是dataK 这里避免了脏读(读取B未提交的数据)、
7. select dataK -- 当B提交事务后 查询到的还是dataK 避免了不可重复读(就是说你这个事务刚开始读取 的数据在事务提交之前不会改变 不管其他事物是否进行了数据更改并且提交 解决了不可重复读)
10.update dataK to dataQ... 更新了dataK这条数据 变为dataQ
}
-- B()
{
3.start transaction; 开启B事务
4.update dataK to dataP ... 更新了dataK这条数据 变为dataP
}
6. commit;
-- C()
{
8. start transaction; 开始C事务
9. select dataP 因为B修改后已经提交了 所有读出来的就是dataP
12. update dataP to dataQ... 更新了dataP这条数据 变为dataM 这时出现阻塞 就是 A事务在更新的时候 只要A没有提交事务 其他的事务就不能对数据进行(删除更新) 操作 就是加了行锁 其他事务可以同时读 但是不能同时写 必须等待行锁释放(事务提交)
}
--------------------------------------------------------------------------------------
3. 幻读
A B两个事务 A先统计整张表的数据 这时B来了更新(或者插入 删除)了一行或者多行数据 A再次统计时发现
表的数据不对了
-- 解决办法 设置隔离级别为 Serializable 本质就是表锁 锁整张表 性能低下
set session transcation isolation level Serializable
A B两个事务
1.当A先事务查询了整张表时(注意是select * from xtable 对整张表进行查询)这时加了(表锁) B事务只能进行查询(条件查询 查整张表都可以) -- 插入 删除 更新(任何一条数据)都必须等待A事务进行commit (这解决了幻读 对整张表的统计的时候出现数据的更改问题)
2. 当A事务查询了某一条数据时 就为这一行数据加上(行锁) 其他事务可以查询这条数据 但是不能更新 删除 这条数据 但是对其他数据没有影响 可以对数据库除这条数据外进行 (插入 删除 修改)
-- Serializable 就是在一个事务读取一条数据或者整张表的数据时 就为数据加锁(行锁或表锁,行锁只锁这一行,表锁锁整张表),其他事物无法操作数据只能读,而Repeatable Read 则是在一个事务操作(更新 删除)这条数据时才会为数据加锁,其他事务必须等待这个事务提交之后才能操作数据 ,读取的时候不会加锁 所有事务都可以读 但是读出的数据在本事务提交前不会变化(无论别的事务是否提交了数据)
事务隔离级别越高 执行效率越低
read uncommitted |不能避免脏读不可重复读幻读
read committed 避免脏读 |不能避免不可重复读 幻读
repeatable read 避免脏读/不可重复读 |不可避免幻读
serializable 避免脏读 不可重复读 幻读 |
安全性 serializable > repeatable read > read committed
并发行 read committed > repeatable read >serializable
更新丢失:
如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。
持久性(Durability): 指一个事务一旦提交,它对数据库中数据的改变就是永久性的 不能回滚
2.锁
共享锁
每执行一次查询语句(非简单查询) 为查询的表加上共享锁 一个表可以存在多个共享锁 即所有事物都可以进行读操作 但能进行修改 (除非释放锁)
排它锁(互斥锁)
更新 删除等DML操作时 为表的一行记录添加排它锁 不能在表上加共享锁和其他的排它锁 别的事务要想操作数据 必须等待锁释放(事务提交)
意向锁
更新锁
计划锁
悲观锁
乐观锁
为每条记录增加一个版本号
4.索引
-
索引是一种数据库对象 按照索引检索效率高
-
索引要占用一定的空间 在执行检索语句是如果符合了索引规则 自动按照索引进行检索
-
索引被删除或被损坏 不会对表产生影响 影响的只是查询的速度
-
MySQL索引使用两种数据结构 B+Tree 和 Hash
3.1B+Tree和Hash (PDF文档)
Mysql 索引默认是B+Tree
平衡二叉树: 左右子树深度查最高不能超过1
BTree和Hash索引的区别
- Hash只能满足 = in <> 的条件 而 > < like 不能满足 BTree可以满足
- 由于Hash是无序的 无法避免数据排序操作
- Hash索引建了组合索引(a,x)只能满足组合查询的条件 单个条件不满足 因为Hash索引是将组合数据的HashCode存储到Hash表中
BTree的特点(PDF)
3.2索引类型
1.主键索引
创建一张表时 当指定了某一列是主键时 MySQL自动创建主键索引
2.唯一索引
创建一张表时 当指定了某一列有唯一(unique)约束时 MySQL自动创建唯一索引
3.普通索引
3.全文索引 (fulltext)
3.3创建方式
索引命名规范
- 主键索引 命名pk_
- 唯一索引 uk_
- 普通索引 idx_
1.主键索引的添加
1. 表创建的时候指定主键
create table xx(
id int primary key, -- 直接指定主键 不推荐
constraint pk_id primary key(id) -- 一般使用第二种 推荐
)
2. 建表之后增加主键约束
Alter table xx add constraint pk_id primary key(id)
删除主键约束
alter table xx drop primary key key_name
2.唯一索引的添加
1. 表创建的时候指定唯一约束的列
create table xx(
id int unique, -- 直接指定唯一约束 不推荐
constraint uni_id unique(id) -- 一般使用第二种 推荐
)
2.建表之后增加唯一约束
Alter table xx ADD constraint uni_id unique(id)
或者
create unique index uni_id on 表名(字段名(长度))
删除唯一约束
alter table tableName drop index key_name
3.普通索引的添加
1.建表是创建
create table a(
id int;
name varchar(12)
key idx_name(name )
)
2.建表后创建
create index idx_id_name on tableName(id,name);
4.全文索引
索引的查询语句
show index from tableName;
索引的删除
alter table tableName drop index index_name
删除主键索引
alter table tableName drop primary key(主键索引名 如果只有一个主键 可以不写主键索引名)
5.表引擎
MyISAM查询速度快
1.MyISAM 不支持事务 InnoDB支持事务
2.MyISAM不支持外键约束 InnoDB支持外键约束
3.MyISAM 使用非聚集索引 索引和数据是分离的 在BTree索引的叶子节点中存储的就是数据的地址,
InnoDB 使用聚集索引 (每个表一定有主键 没有设置主键系统会分配一个 外部看不见) 存数据的时候就直接以BTree方式来存 关键字就是主键(rolid) 通过主键索引查找也能查到文件位置 设置了其他索引的话 通过索引先找到主键 在通过主键查找到元素)
4.MyISAM支持全文索引 InnoDB5.6之后支持
5.MyISAM有一个变量单独存储记录数 执行select count()时 直接取出变量值 ---- InnoDB不会存储记录数 执行select count()时 是全文检索
6.MyISAM只支持表级锁 InnoDB支持行级锁和表级锁
7.缓存 MyISAM只缓存索引 不缓存真实数据 InnoDB不仅缓存索引还缓存真实数据 对内存要求较高 而且内存大小对性能有决定性的影响
修改表引擎
ALTER TABLE tablename ENGINE = MyISAM
显示所有的引擎
show engines;
数据库设计的三范式
第一范式(1NF):单元不可分割 (列原子性),一列的数据不可再分 示例
技能 |
---|
C ,C++ ,Java, |
第二范式(2NF):主键依赖,通过主键确定一条记录
第三范式 (3NF):取消传递依赖,在确定一条记录时不通过传递的列来确定 (表进行分离 单独)
数据库连接池
数据库连接池的工作原理?
当应用程序启动时,创建一个连接池,在连接池中创建一定数量的连接,当应用程序获得这个连接时,连接的状态变为忙状态,当连接的数量小于设置的空闲连接数,将创建新的连接,但连接数量不能超过设置的最大连接数量。应用程序用完连接后,连接变成空闲状态。当没有空闲连接,应用程序还获得连接时将等待。