MySQL数据库笔记总结
mysql数据库总结
一、数据库简介
1. 数据
所谓数据(data)是指对客观事物进行描述并可以鉴别的符号,这些符号是可识别的、抽象的。它不仅仅指狭义上的数字,而是有多种表现形式:字母、文字、文本、图形、音频、视频等。现在计算机存储和处理的数据范围十分广泛,而描述这些数据的符号也变得越来越复杂了。
2. 数据库
数据库(database,db)指的是以一定格式存放、能够实现多个用户共享、与应用程序彼此独立的数据集合。举例:车库,水库,数据库
3. 数据库管理系统
数据库管理系统(database management system,dbms)是用来定义和管理数据的软件。如何科学的组织和存储数据,如何高效的获取和维护数据,如何保证数据的安全性和完整性,这些都需要靠数据库管理系统完成。目前,比较流行的数据库管理系统有:oracle、mysql、sql server、db2等。
4. 数据库应用程序
数据库应用程序(database application system,dbas)是在数据库管理系统基础上,使用数据库管理系统的语法,开发的直接面对最终用户的应用程序,如学生管理系统、人事管理系统、图书管理系统等。
5. 数据库管理员
数据库管理员(database administrator,dba)是指对数据库管理系统进行操作的人员,其主要负责数据库的运营和维护。
6. 最终用户
最终用户(user)指的是数据库应用程序的使用者。用户面向的是数据库应用程序(通过应用程序操作数据),并不会直接与数据库打交道。
7. 数据库系统
数据库系统(database system,dbs)一般是由数据库、数据库管理系统、数据库应用程序、数据库管理员和最终用户构成。其中dbms是数据库系统的基础和核心。
二、数据库分类
(一)、数据库的分类
数据库经过几十年的发展,出现了多种类型。根据数据的组织结构不同,主要分为网状数据库、层次数据库、关系型数据库和非关系型数据库四种。网状数据库、层次数据库已经逐渐被淘汰,目前最常见的数据库模型主要是:关系型数据库和非关系型数据库。
1、关系型数据库
关系型数据库模型是将复杂的数据结构用较为简单的二元关系(二维表)来表示。在该类型数据库中,对数据的操作基本上都建立在一个或多个表格上,我们可以采用结构化查询语言(sql)对数据库进行操作。关系型数据库是目前主流的数据库技术,其中具有代表性的数据库管理系统有:oracle、db2、sql server、mysql等。
2、非关系型数据库nosql
nosql(not only sql)泛指非关系型数据库。关系型数据库在超大规模和高并发的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题。nosql数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。常见的非关系型数据库管理系统有memcached、mongodb、redis等。
(二)、常见的关系型数据库
1、oracle
oracle数据库是由美国的甲骨文(oracle)公司开发的世界上第一款支持sql语言的关系型数据库。经过多年的完善与发展,oracle数据库已经成为世界上最流行的数据库,也是甲骨文公司的核心产品。
oracle数据库具有很好的开放性,能在所有的主流平台上运行,并且性能高、安全性高、风险低;但是其对硬件的要求很高、管理维护和操作比较复杂而且价格昂贵,所以一般用在满足对银行、金融、保险等行业大型数据库的需求上。
2、db2
db2是ibm公司著名的关系型数据库产品。db2无论稳定性,安全性,恢复性等等都无可挑剔,而且从小规模到大规模的应用都可以使用,但是用起来非常繁琐,比较适合大型的分布式应用系统。
3、 sql server
sql server是由microsoft开发和推广的关系型数据库,sql server的功能比较全面、效率高,可以作为中型企业或单位的数据库平台。sql server可以与windows操作系统紧密继承,无论是应用程序开发速度还是系统事务处理运行速度,都能得到大幅度提升。但是,sql server只能在windows系统下运行,毫无开放性可言。(不能跨平台,oracle,mysql都是跨平台的)
4、mysql
瑞典 mysql ab 公司开发,目前属于 oracle 旗下产品(mysql先是被sun收购了,然后sun被oracle收购了)。mysql是一种开放源代码的轻量级关系型数据库,mysql数据库使用最常用的结构化查询语言(sql)对数据库进行管理。由于mysql是开放源代码的,因此任何人都可以在general public license的许可下下载并根据个人需要对其缺陷进行修改。
由于mysql数据库体积小、速度快、成本低、开放源码等优点,现已被广泛应用于互联网上的中小型网站中,并且大型网站也已经使用mysql数据库,如网易、新浪等。
三、基本操作
1、sql语言的五个部分
数据查询语言(data query language,dql):dql主要用于数据的查询,其基本结构是:
使用select子句,from子句和where子句的组合来查询一条或多条数据。
数据操作语言(data manipulation language,dml):dml主要用于对数据库中的数据进行增加、修改和删除的操作,其主要包括:
1) insert:增加数据
2) update:修改数据
3) delete:删除数据
数据定义语言(data definition language,ddl):ddl主要用针对是数据库对象(表、索引、视图、触发器、存储过程、函数、表空间等)进行创建、修改和删除操作。其 主要包括:
1) create:创建数据库对象
2) alter:修改数据库对象
3) drop:删除数据库对象
数据控制语言(data control language,dcl):dcl用来授予或回收访问数据库的权限,其主要包括:
1) grant:授予用户某种权限
2) revoke:回收授予的某种权限
事务控制语言(transaction control language,tcl):tcl用于数据库的事务管理。 其主要包括:
1) start transaction:开启事务
2) commit:提交事务
3) rollback:回滚事务
4) set transaction:设置事务的属性
注意: dml和ddl的不同
数据操作语言(dml)(insert、update、delete)针对表中的数据。
而数据定义语言(ddl)(create、alter、drop)针对数据库对象,比如数据库database、表table、索引index、视图view、存储过程procedure、触发器trigger。
四、常用语句
(一)、单表查询
#创建部门和员工表 #下面的sql就是创建了四张表:-- 单行注释 -- 下面的sql就是创建了四张表:-- 单行注释 -- ctrl+shift+r 运行快捷键 /* 多行注释 */ create table dept ( deptno int(2) not null, dname varchar(14), loc varchar(13) ); alter table dept add constraint pk_dept primary key (deptno); create table emp ( empno int(4) primary key, ename varchar(10), job varchar(9), mgr int(4), hiredate date, sal double(7,2), comm double(7,2), deptno int(2) ); alter table emp add constraint fk_deptno foreign key (deptno) references dept (deptno); create table salgrade ( grade int primary key, losal double(7,2), hisal double(7,2) ); create table bonus ( ename varchar(10), job varchar(9), sal double(7,2), comm double(7,2) ); commit; insert into dept (deptno, dname, loc) values (10, 'accounting', 'new york'); insert into dept (deptno, dname, loc) values (20, 'research', 'dallas'); insert into dept (deptno, dname, loc) values (30, 'sales', 'chicago'); insert into dept (deptno, dname, loc) values (40, 'operations', 'boston'); commit; insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7369, 'smith', 'clerk', 7902, '1980-12-17', 800, null, 20); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7499, 'allen', 'salesman', 7698, '1981-02-20', 1600, 300, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7521, 'ward', 'salesman', 7698, '1981-02-22', 1250, 500, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7566, 'jones', 'manager', 7839, '1981-04-02', 2975, null, 20); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7654, 'martin', 'salesman', 7698, '1981-09-28', 1250, 1400, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7698, 'blake', 'manager', 7839, '1981-05-01', 2850, null, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7782, 'clark', 'manager', 7839, '1981-06-09', 2450, null, 10); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7788, 'scott', 'analyst', 7566, '1987-04-19', 3000, null, 20); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7839, 'king', 'president', null, '1981-11-17', 5000, null, 10); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7844, 'turner', 'salesman', 7698, '1981-09-08', 1500, 0, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7876, 'adams', 'clerk', 7788, '1987-05-23', 1100, null, 20); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7900, 'james', 'clerk', 7698, '1981-12-03', 950, null, 30); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7902, 'ford', 'analyst', 7566, '1981-12-03', 3000, null, 20); insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (7934, 'miller', 'clerk', 7782, '1982-01-23', 1300, null, 10); commit; insert into salgrade (grade, losal, hisal) values (1, 700, 1200); insert into salgrade (grade, losal, hisal) values (2, 1201, 1400); insert into salgrade (grade, losal, hisal) values (3, 1401, 2000); insert into salgrade (grade, losal, hisal) values (4, 2001, 3000); insert into salgrade (grade, losal, hisal) values (5, 3001, 9999); commit;
1、基本查询
-- 对员工表进行操作: -- 简单的查询: -- 查看员工表所有数据: select * from emp;-- *代表查询所有字段 #运行方式1:选中+右键-- 运行异选中 #运行方式2:ctrl+shift+r -- 查看员工表的员工名字,员工编号,工资。(部分字段) select ename,empno,sal from emp; -- 不区分大小写: select ename,empno,sal from emp;-- 对于关键字,字段名,表名 这些不区分大小写。 -- 使用算术表达式: select ename,empno,sal,sal*12+50000 from emp; -- 使用别名: select ename 员工姓名,empno 员工编号,sal 月工资,sal*12+50000 年薪 from emp; select ename '员工姓名',empno '员工编号',sal '月工资',sal*12+50000 '年薪' from emp;-- 单引号可以 select ename "员工姓名",empno "员工编号",sal "月工资",sal*12+50000 "年薪" from emp;-- 双引号可以 select ename as 员工姓名,empno as 员工编号,sal as 月工资,sal*12+50000 as 年薪 from emp;-- 中间加上as可以 select ename as '员工 姓名',empno as '员工+编号',sal as '(月工资)',sal*12+50000 as '年薪' from emp; -- 如果你的别名中有特殊符号,那么单引号或者双引号必须加上,但是你的别名中要是没有特殊符号,那么单双引号都可以省略不写。 -- 去重 distinct -- 查看有哪些职位: select distinct job from emp; -- 查看有几个职位: select count(distinct job) 职位个数 from emp; select distinct job,sal from emp;-- 组合起来是不重复的 select distinct (job,sal) from emp;-- ()不用写,就是这个含义,但是写了反倒出错了! -- 查看员工的名字,工资:按照工资排序 select ename,sal from emp; select ename,sal from emp order by sal;-- 默认升序 select ename,sal from emp order by sal asc;-- 升序 asc select ename,sal from emp order by sal desc;-- 降序 desc select * from emp order by sal asc,hiredate desc;-- 先按照sal升序排列,在sal相同的情况下,按照hiredate降序排列
2、where条件查询
select * from emp; #where简单查询: select * from emp where sal>2500; select * from emp where sal=2500; select * from emp where sal<2500; select * from emp where sal!=2450; select * from emp where sal<>2450; select ename,sal ,deptno,job,hiredate from emp where hiredate<'1982-01-23' -- <代表的是早于82年的数据 select * from emp where job='manager' select * from emp where job="manager" select * from emp where job='manager'-- mysql中在精准查询的时候,发现是忽略大小写查询的 select * from emp where binary job='manager'-- 想要精准的查大小写的话,要在前面加上关键词:binary -- and select * from emp where sal>1000 and sal<3000; select * from emp where sal>=1000 and sal<=3000; select * from emp where sal>=1000 && sal<=3000; select * from emp where sal between 1000 and 3000;-- 等效sal>=1000 and sal<=3000; select * from emp where sal not between 1000 and 3000; -- or select * from emp where deptno=30 and deptno=10;-- 查询不到数据 select * from emp where deptno=30 or deptno=10; select * from emp where deptno=30 || deptno=10; select * from emp where deptno in (30,10); select * from emp where deptno not in (30,10); -- 模糊查询: -- 查询名字中带字母s的数据: select * from emp where ename='s';-- 精准查询,查询名字为s的 select * from emp where ename='%s%';-- 模糊查询 %0-n个字符 中间用=号不行 select * from emp where ename like '%o%';-- 查询忽略大小写 select * from emp where binary ename like '%o%';-- 关注大小写,前面加上binary -- 查询名字中第二个字母是o的: select * from emp where binary ename like '_o%';-- _代表一个字符 -- 查询名字中第三个字母是o的: select * from emp where binary ename like '__o%'; -- sql执行顺序 select * from emp where job='salesman' or job='manager' and deptno=10; select * from emp where (job='salesman' or job='manager') and deptno=10; select * from emp where job='salesman' or (job='manager' and deptno=10);-- 等价这个 -- 结论:and的运算的优先级别高于or,但是不要去记忆,我们写sql直接用()来控制优先顺序即可
3、函数
(1)、字符串函数 (string stringbuilder)
函数 | 描述 |
concat(str1, str2, ···, strn) | 将str1、str2···strn拼接成一个新的字符串 |
insert(str, index, n, newstr) | 将字符串str从第index位置开始的n个字符替换成字符串newstr |
length(str) | 获取字符串str的长度 |
lower(str) | 将字符串str中的每个字符转换为小写 |
upper(str) | 将字符串str中的每个字符转换为大写 |
left(str, n) | 获取字符串str最左边的n个字符 |
right(str, n) | 获取字符串str最右边的n个字符 |
lpad(str, n, pad) | 使用字符串pad在str的最左边进行填充,直到长度为n个字符为止 |
rpad(str, n, pad) | 使用字符串pad在str的最右边进行填充,直到长度为n个字符为止 |
ltrim(str) | 去除字符串str左侧的空格 |
rtrim(str) | 去除字符串str右侧的空格 |
trim(str) | 去除字符串str左右两侧的空格 |
replace(str, oldstr, newstr) | 用字符串newstr替换字符串str中所有的子字符串oldstr |
reverse(str) | 将字符串str中的字符逆序 |
strcmp(str1, str2) | 比较字符串str1和str2的大小 |
substring(str, index, n) | 获取从字符串str的index位置开始的n个字符 |
(2)、数值函数 (math)
函数 | 描述 |
abs(num) | 返回num的绝对值 |
ceil(num) | 返回大于num的最小整数(向上取整) |
floor(num) | 返回小于num的最大整数(向下取整) |
mod(num1, num2) | 返回num1/num2的余数(取模) |
pi() | 返回圆周率的值 |
pow(num, n)/power(num, n) | 返回num的n次方 |
rand(num) | 返回0~1之间的随机数 |
round(num, n) | 返回x四舍五入后的值,该值保留到小数点后n位 |
truncate(num, n) | 返回num被舍去至小数点后n位的值 |
(3)、日期与时间函数1. 数值函数 (date、dateformat、calendar)
函数 | 描述 |
curdate() | 返回当前日期 |
curtime() | 返回当前时间 |
now() | 返回当前日期和时间 |
sysdate() | 返回该函数执行时的日期和时间 |
dayofyear(date) | 返回日期date为一年中的第几天 |
week(date)/weekofyear(date) | 返回日期date为一年中的第几周 |
date_format(date, format) | 返回按字符串format格式化后的日期date |
date_add(date, interval expr unit)/adddate(date, interval expr unit) |
返回date加上一个时间间隔后的新时间值 |
date_sub(date, interval expr unit)/subdate(date, interval expr unit) |
返回date减去一个时间间隔后的新时间值 |
datediff(date1, date2) | 返回起始日期date1与结束日期date2之间的间隔天数 |
(4)、其他函数
函数 | 描述 |
database() | 返回当前数据库名 |
version() | 返回当前mysql的版本号 |
user() | 返回当前登录的用户名 |
inet_aton(ip) | 返回ip地址的数字表示 |
inet_ntoa | 返回数字代表的ip地址 |
password(str) | 实现对字符串str的加密操作 |
format(num, n) | 实现对数字num的格式化操作,保留n位小数 |
convert(data, type) | 实现将数据data转换成type类型的操作 |
-- dual伪表: select 1+1 计算结果 from dual; -- 字符函数: select length('abc') from dual; select lower('abc') from dual; select upper('abc') from dual; select replace('abcaig','a','o') from dual; select substring('abcdef',4,2) from dual; -- 数值函数: select abs(-4) from dual; select ceil(9.1) from dual; select floor(10.9) from dual; select mod(10,3) from dual; select pow(3,2) from dual; select rand() from dual; select round(6.4) from dual; -- 日期函数: select now(),sysdate(),sleep(3),sysdate() from dual; -- 其他函数: select database() from dual; select version() from dual; select user() from dual; select inet_aton('192.168.110.253') from dual; select inet_ntoa(3232274941) from dual; select password('abc') from dual;
(5)、流程函数(if、switch)
函数 | 描述 |
if(condition, t, f) | 如果条件condition为真,则返回t,否则返回f |
ifnull(value1, value2) | 如果value1不为null,则返回value1,否则返回value2 |
nullif(value1, value2) | 如果value1等于value2,则返回null,否则返回value1 |
case value when [value1] then result1 [when [value2] then result2 ...] [else result] end | 如果value等于value1,则返回result1,···,否则返回result |
case value when [value1] then result1 [when [value2] then result2 ...] [else result] end | 如果value等于value1,则返回result1,···,否则返回result |
case when [condition1] then result1 [when [condition2] then result2 ...] [else result] end | 如果条件condition1为真,则回result1,···,否则返回result |
-- 流程函数: select if(1>10,'a','b') from dual; select * from emp; select if(sal>800,'这个人工资大于800','这个人工资小于等于800') from emp; select ename,job,sal,comm,sal+comm from emp; select ename,job,sal,comm,sal+ifnull(comm,0) from emp; select nullif(13,139) from dual; select ename, case job when 'salesman' then '销售' when 'clerk' then '职员' when 'manager' then '经理' else '其他职位' end from emp; select ename, job, sal, case when sal>=500 and sal<=800 then '工资最低的人' when sal>800 and sal<=2750 then '工资稍微高一点点' else '工资比较高' end from emp;
(6)、json函数
函数 | 描述 |
json_append() | 在json文档中追加数据 |
json_insert() | 在json文档中插入数据 |
json_replace () | 替换json文档中的数据 |
json_remove () | 从json文档的指定位置移除数据 |
json_contains() | 判断json文档中是否包含某个数据 |
json_search() | 查找json文档中给定字符串的路径 |
(7)、多行函数
多行函数 | 描述 |
count() | 统计表中记录的数目 |
sum() | 计算指定字段值的总和 |
avg() | 计算指定字段值的平均值 |
max() | 统计指定字段值的最大值 |
min() | 统计指定字段值的最小值 |
-- 多行函数:sum,avg,count,max,min -- 用一句话将所有多行函数表示: select max(sal),min(sal),sum(sal),avg(sal),sum(sal)/count(*),sum(sal)/count(1),count(*),count(1) from emp; select max(ename),min(ename),sum(ename),avg(ename),count(ename) from emp; -- max,min,count可以修饰所有类型 -- sum,avg 仅限于修饰 数值类型 -- count:计数: -- 统计有多少个员工: select count(ename) from emp; select count(job) from emp; select count(comm) from emp;-- 计数 null值不会被计数 -- 最好不要用普通字段来统计全表的数据的个数,因为不知道哪个字段中有null值 select * from emp; select count(*) from emp; select 1 from emp; select count(1) from emp;-- 效率高 直接计算1的个数 select * from emp; -- 写sql判断是否正确: select avg(sal) from emp;-- 对 select avg(sal),ename from emp;-- 不对 select avg(sal),lower(ename) from emp;-- 不对 -- 结论:多行函数不能跟普通字段和单行函数一起使用 -- 计算部门个数: select deptno from emp; select count(deptno) from emp; select count(distinct deptno) from emp;
4、group by 和 having
-- group by 子句 分组 having子句 分组后二次筛选 -- 统计各个部门的平均工资(只显示平均工资2000以上的) select avg(sal) from emp; -- 1条记录-- 不是各个部门的,是所有部门的 select deptno from emp; -- 14条记录 select deptno,avg(sal) from emp; -- 14? 1! 字段不能和分组函数共存,除非该字段是分组group by字段 select deptno, avg(sal) from emp group by deptno; select deptno, avg(sal) from emp -- where avg(sal) >2000 -- where子句不能使用多行函数(分组函数) group by deptno having avg(sal)>2000 -- 各个子句的书写是有顺序的 order by avg(sal) desc; -- 统计各个岗位的平均工资,除了manager select job, avg(sal),count(1) from emp group by job having job !='manager' order by avg(sal); select job,avg(sal),count(1) from emp where count(1) >3 -- where子句不能使用多行函数(分组函数) group by job;-- 该句错误 -- 总结select语句 -- 1.where子句和having子句联系和区别 -- 联系: 都是筛选记录 -- 区别:1.where是groupby 之前,having是groupby之后 -- 2.where不能出现多行函数,having中可以出现多行函数 -- 2:select各个子句的书写顺序 select job,avg(sal),count(1) from emp where job !='manager' group by job having avg(sal)>2000 order by count(1); -- 3:select各个子句的执行顺序 -- from where group by select having order by -- 注意:1.最后执行的是order by!!!! 分页 -- 2.select子句的执行顺序 -- 4.为什么where不能出现多行函数,having中可以出现多行函数 -- group by 之后的子句都可以使用多行函数,之前的子句不可以使用多行函数 -- 扩展四个分组和having的语句练习 -- 列出工资最小值小于2000的职位 -- min(sal) job min(sal)<2000 -- 获取各个职位的最小工资 select job,min(sal) from emp group by job order by min(sal); -- 获取各个职位的最小工资,筛选出小于2000的 select job,min(sal) from emp group by job having min(sal)<2000 order by min(sal); -- 列出平均工资大于1200元的部门和工作搭配组合 -- avg(sal)>1200 job deptno select deptno,job,avg(sal),count(1) from emp group by deptno,job having avg(sal) > 1200 order by avg(sal); -- 统计[人数小于4的]部门的平均工资。 select deptno,count(1),avg(sal) from emp group by deptno having count(1)<4; -- 统计各部门的最高工资,排除最高工资小于3000的部门。 select deptno,max(sal) from emp group by deptno having max(sal) >=3000;
(二)、多表查询
1、sql92版
-- 查询员工的工资,姓名 select sal,ename from emp; -- 查询员工的部门编号,部门名称 select deptno,dname from dept; -- 查询员工的工资,姓名,部门编号,部门名称:---需要多表查询: -- 按时间划分:sql92 和sql99 -- 多表查询:sql92 -- 查询员工的工资,姓名,部门编号,部门名称: select * from emp,dept; 注意特点: (1)两个表中的同名字段,没有合成为一个字段,而是各自展示:一个为deptno,一个为deptno1 (2)表中有很多数据:有14*4=56(笛卡尔积)条记录:但是有很多数据都是没有意义的 -- 两表查询: -- 内连接:(等值连接) -- 笛卡尔乘积没有意义,但是我们可以通过过滤让出来的数据都是有意义的: select * from emp,dept where emp.deptno=dept.deptno; select empno,ename,job,dname,loc from emp,dept where emp.deptno=dept.deptno; select empno,ename,job,dname,loc from emp e,dept d where e.deptno=d.deptno; select empno,ename,job,dname,loc,deptno from emp e,dept d where e.deptno=d.deptno;-- 不行 select empno,ename,job,dname,loc,e.deptno from emp e,dept d where e.deptno=d.deptno;-- 行,公共字段一定要加上所属的表名 select e.empno,e.ename,e.job,d.dname,d.loc,e.deptno from emp e,dept d where e.deptno=d.deptno; -- 建议每个字段都加上所属表名,一开始就指定了名字 提高查询效率 -- 内连接:(非等值连接) -- 查询员工的姓名,工资,工资的等级: select * from emp; select * from salgrade; select * from emp e,salgrade s where e.sal>=s.losal and e.sal<=s.hisal; -- 内连接:(自连接) -- 查询员工的姓名,岗位,上级领导编号,上级领导名字; select * from emp; select e1.ename,e1.job,e1.mgr,e2.ename from emp e1,emp e2 where e1.mgr=e2.empno; -- 三表查询: -- 查询员工的姓名,职位,工资,工资等级,部门编号,部门名称: select e.ename,e.job,e.sal,s.grade,e.deptno,d.dname from emp e,dept d,salgrade s where e.deptno=d.deptno and e.sal>=s.losal and e.sal<=s.hisal; /* 总结 sql92: 1、笛卡尔积:将所有数据进行连接,但是许多数据都是没有意义的 2、内连接:等值连接(两表查询、三表查询),非等值连接,自连接 3、外连接:mysql对于92语法没有外连接 */
2、sql99版
/* -- sql99多表查询 -- 注意1:依然可以给表添加别名 -- 注意2;如果使用on或则usering关键字必须对结果进行筛选,必须使用inner join关键字组员于表与表的连接,其中inner可以省去 -- 注意3:外连接的outer关键字可以不写 -- 注意4:依然可以使用排序等关键字 */ -- 查询两个表数据:emp ,dept -- 交叉连接: -- select 内容 from 表名 cross join select * from emp cross join dept; -- 自然连接: -- 使用关键字natrual join,将不同表中的同名字段自动连接 -- 使用:select 内容 from 表名 natural join 表名 select * from emp natural join dept; /* 特点1:底层是笛卡尔积,按照同名同值字段自动进行等值筛选 缺点1:如果想按照字段名不同,但是字段值不同筛选怎么办? 缺点2:如果只想按照部分字段结果筛选怎么办? */ /* 解决1:使用using关键字,将两个表中同名的deptno字段进行连接 作用1:指明使用指定的字段对联合查询的结果进行等值筛选 注意:指明的字段必须是两边同名同值字段 使用:select 内容 from 表名 inner join 表名 using(字段名) */ -- 内连接:inner join-using select * from emp inner join dept using(deptno); /* 解决2:使用on关键字,进行自定义链接查询 注意:普通筛选使用where筛选不使用on 好处:便于阅读 使用:select 内容 from 表名 inner join 表名 on 连接条件 where 普通筛选条件 */ select * from emp inner join dept on emp.deptno=dept.deptno where job='salesman'; -- 内连接:inner join-on:等值连接 select e.ename,e.job,e.deptno,d.dname from emp e inner join dept d on (e.deptno=d.deptno and e.deptno>10); select e.ename,e.job,e.deptno,d.dname from emp e inner join dept d on (e.deptno=d.deptno) where e.deptno>10; -- 上面两种写法虽然结果一样,但是我们建议用下面的,为啥? -- on后面连的条件就是 表连接的条件 -- where后面连的条件就是 表的过滤条件 -- 内连接:inner join-on:非等值连接 -- 查询员工的姓名,职位,工资,工资等级: select * from emp e inner join salgrade s on (e.sal>=s.losal and e.sal<=s.hisal); -- 内连接:inner join-on:自连接 -- 查询员工的名字,职位,上级领导编号,上级领导名称: select e1.ename 名字,e1.job 职位,e1.mgr 上级领导编号,e2.ename 上级领导名称 from emp e1 inner join emp e2 on (e1.mgr=e2.empno); -- 内连接:inner join-on:三表连接: -- 查看员工的姓名,工资,工资等级,部门编号,部门名称: select * from emp inner join dept on emp.deptno=dept.deptno inner join salgrade on emp.sal>=salgrade.losal and emp.sal<=salgrade.hisal; -- 现在的逻辑会出现:有的部门没有员工,有的员工没有部门 -- 左外连接 select * from emp e left outer join dept d on (e.deptno=d.deptno);-- 左外---左表数据是全的 -- 右外连接 select * from emp e right outer join dept d on (e.deptno=d.deptno);-- 右外--右表数据是全的 -- 全外连接:mysql中没有全外连接: select * from emp e full outer join dept d on (e.deptno=d.deptno);-- 在oracle中应该出现15条记录 /* 总结 sql99: 1、交叉连接:得到笛卡尔积,但是很多数据都是无用的。 2、自然连接:会连接所有的同名字段。 3、内连接: inner join - using:可以指定用哪个同名字段 using(deptno) - - -> emp.deptno=dept.deptno inner jion - on:等值连接(两表查询、三表查询),非等值连接,自连接 4、外连接:左外连接,右外连接 mysql99语法中没有全外连接 */
3、子查询,集合查询,分页查询
(1)、不相关子查询
-- 单行子查询:子查询的结果只有一行 -- 查得所有【比“clark”工资高的】员工的信息 select sal from emp where ename ='clark' --2450 select * from emp where sal > 2450.00; select * from emp where sal > (select sal from emp where ename ='clark'); -- 特点:一条sql语句含有多个select,先执行子查询,再执行外查询;子查询可以对立运行 -- 查询【工资高于平均工资的】雇员名字和工资。 select ename,sal from emp where sal >(select avg(sal) from emp ); -- 查询【和clark同一部门且比他工资低的】雇员名字和工资。 select ename ,sal,deptno from emp where deptno = (select deptno from emp where ename='clark') and sal < (select sal from emp where ename ='clark'); -- 查询[职务和scott相同,比scott雇佣时间早的]雇员信息 select * from emp where job = (select job from emp where ename ='scott') and hiredate < (select hiredate from emp where ename ='scott'); -- 查询工资比scott高或者雇佣时间比scott早的雇员的编号和名字 -- 多行子查询 -- 子查询的结果是多条记录,此时不能之间使用=,< > 需要借助 any all in来实现 -- 查询工资低于任何一个“clerk”的工资的雇员信息。 -- select sal from emp where job ='clerk' -- 1300 1100 950 800 select * from emp where sal< (select max(sal) from emp where job ='clerk'); select * from emp where sal< any(select sal from emp where job ='clerk'); -- 查询[工资比所有的“salesman”都高的]雇员的编号、名字和工资。 select max(sal) from emp where job ='salesman'; -- all >all select empno,ename,sal from emp where sal >(select max(sal) from emp where job ='salesman'); select empno,ename,sal from emp where sal > all(select sal from emp where job ='salesman'); --查询部门20中职务同部门10的雇员一样的雇员信息。 select job from emp where deptno = 20; -- clerk analyst select job from emp where deptno = 10; -- manager president clerk ---- clerk manager -- in 等于其中任何一个 select * from emp where deptno = 20 and job in (select job from emp where deptno = 10);
(2)、相关子查询
-- 相关子查询 -- 查询最高工资的员工 select * from emp where sal = (select max(sal) from emp) -- 不相关的子查询:子查询可以独立运行,先运行子查询,再运行外查询 -- 查询10部门最高工资的员工 -- select max(sal) from emp where deptno = 10 select * from emp where deptno = 10 and sal = (select max(sal) from emp where deptno = 10) -- 查询20部门最高工资的员工 select * from emp where deptno = 20 and sal = (select max(sal) from emp where deptno = 20) -- 查询30部门最高工资的员工 select * from emp e1 where e1.deptno = 30 and sal = (select max(sal) from emp e2 where e2.deptno = 30) -- 查询每个部门最高工资的员工 select * from emp e1 where sal = (select max(sal) from emp e2 where e2.deptno = e1.deptno) -- 相关子查询:子查询不可以独立运行,并且先运行外查询,再运行子查询 -- 好处:简单 功能强大(一些使用不相关子查询不能实现或者实现繁琐的子查询,可以使用相关子查询实现) -- 缺点:稍难理解 -- 查询工资高于平均工资的那些员工 select avg(sal) from emp select * from emp where sal > ( select avg(sal) from emp) -- 查询[工资高于其所在部门的平均工资的]那些员工 -- 查询[工资高于其所在部门10的平均工资的]那些员工 select avg(sal) from emp where deptno = 10 select * from emp where deptno = 10 and sal >2916.66666666667 select * from emp e1 where e1.deptno = 10 and sal >(select avg(sal) from emp e2 where e2.deptno = 10) -- 查询[工资高于其所在部门20的平均工资的]那些员工 select * from emp e1 where e1.deptno = 20 and sal >(select avg(sal) from emp e2 where e2.deptno = 10) -- 查询[工资高于其所在部门30的平均工资的]那些员工 select * from emp e1 where e1.deptno = 30 and sal >(select avg(sal) from emp e2 where e2.deptno = 30) select * from emp e1 where sal >(select avg(sal) from emp e2 where e2.deptno = e1.deptno)order by deptno /* 总结:区分相关子查询和不相关子查询 不相关的子查询:子查询可以独立运行,先运行子查询,再运行外查询 相关子查询:子查询不可以独立运行,并且先运行外查询,再运行子查询 好处:简单 功能强大(一些使用不相关子查询不能实现或者实现繁琐的子查询,可以使用相关子查询实现) 缺点:稍难理解 */ -- 子查询和连接查询共同使用 -- 查询每个部门平均薪水的等级 -- 子查询不仅可以出现在where条件中,还可以出现在from中 -- 查询每个部门平均薪水 select deptno,avg(sal) from emp group by deptno; -- 查询[每个人的薪水]的等级 select * from emp e join salgrade sg on e.sal between sg.losal and sg.hisal; -- 查询[每个部门平均薪水]的等级 select * from ( select deptno,avg(sal) asl from emp group by deptno) asg join salgrade sg on asg.asl between sg.losal and sg.hisal;
(3)、集合查询
a、集合查询是什么
集合查询就是使用三种集合操作符将两个或者两个以上的查询语句连接起来成为一条查询语句的查询方法。
b、三种集合运算符
-----并集:union all---返回各个查询的所有记录,包括重复记录。(不去重)
union---返回各个查询的所有记录,不包括重复记录。(去重)
-----交集:intersect---返回两个查询共有的记录。(mysql中不含有交集操作)
-----差集:minus---返回第一个查询检索出的记录减去第二个查询检索出的记录之后剩余的记录。(mysql中不含有差集操作)
当使用集合操作的时候,要注意:查询所返回的列数以及列的类型必须匹配,列名可以不同。
-- 1.员工姓名中是否含有a和m -- 求emp表ename中含’a‘或含有‘m’ select * from emp where ename like '%a%' union all select * from emp where ename like '%m%';-- (12条记录) select * from emp where ename like '%a%' union select * from emp where ename like '%m%';-- (9条记录) -- 没有重复数据所以用union和unionall暂时看不出来区别,都是并集的意思 -- 查询员工在部门10和部门20的员工的信息: select * from emp where deptno=10 union select * from emp where deptno=20;-- (8条结果) select * from emp where deptno=10 union all select * from emp where deptno=20;-- (8条结果) -- 注意:union 因为会将各查询子集的记录做比较,故比起union all ,通常速度都会慢上许多。 -- 一般来说,如果使用union all能满足要求的话,务必使用union all。 -- union和unionall都是并集操作,但是unionall不去重,union去重 -- 求emp表ename中即含’a‘又含有‘m’ (mysql中不含有交集操作) select * from emp where ename like '%a%' intersect select * from emp where ename like '%m%'; -- 求emp表ename中即含’a‘但不含有‘m’ (mysql中不含有差集操作) select * from emp where ename like '%a%' minus select * from emp where ename like '%m%';
(4)、分页查询
select * from emp order by sal desc;-- 按照工资进行降序排列: select * from emp order by sal desc limit 3; -- 过滤出来的就是前三条数据 select * from emp order by sal desc limit 0,3; select * from emp order by sal desc limit 3,2; -- 3: 从下标为3开始取数据(索引从0开始) 2: 长度为2 实际分页:一共14条记录,每页5条数据,一共三页 -- 第一页: select * from emp order by sal desc limit 0,5;-- 0:(当前页数-1)*5 -- 第二页: select * from emp order by sal desc limit 5,5;-- 5:(当前页数-1)*5 -- 第三页: select * from emp order by sal desc limit 10,5;-- 10 :(当前页数-1)*5 -- 总结:每页起始索引就是(当前页数-1)*5
(三)、ddl和dml
1、表的创建和操作
表(table)是数据库中数据存储最常见和最简单的一种形式,数据库可以将复杂的数据结构用较为简单的二维表来表示。二维表是由行和列组成的,分别都包含着数据。如图所示:
/* 建立一张用来存储学生信息的表 字段包含学号、姓名、性别,年龄、入学日期、班级,email等信息 学号是主键 = 不能为空 + 唯一 姓名不能为空 性别默认值是男 年龄范围18---30岁 email唯一*/ -- 创建一个数据库表 create table student( sno int(6), name varchar(12), sex char(2), age int(2), enterdate date, clazzname varchar(12), email varchar(20) -- 最后一个字段没有, ); -- 查看表的结构 desc student; -- 查看完整的建表语句 show create table student -- create table `student` ( -- `sno` int(6) default null, -- `name` varchar(12) default null, -- `sex` char(2) default null, -- `age` int(2) default null, -- `enterdate` date default null, -- `clazzname` varchar(12) default null, -- `email` varchar(20) default null -- ) engine=innodb default charset=utf8 -- 查看表的数据 select * from student -- 添加数据 insert into student values(1,'张三丰','男',34,'1256-12-23','武当一班','zhsf@wd.cn'); insert into student values(1,'张三丰','男',34,'1256-12-23','武当一班','zhsf@wd.cn'); insert into student (sno,name) values(12345678,'张三丰'); insert into student (sno,name,enterdate) values(1,'张三丰',now()); insert into student (sno,name,enterdate) values(1,'张三丰',sysdate()); insert into student (sno,name,enterdate) values(1,'张三丰',current_date); insert into student (sno,name,enterdate) values(1,null,"1256/12/23"); update student set sex ='男',age = 64 where sno = 12345678 update student set sno = 2 where sno = 12345678 delete from student where email is null -- delete必须后跟from is null 不是 = null delete from student; /* --char varchar 是字符的个数,不是字节的个数 -- int 宽度是显示的宽度,如果超过,可以自动增大宽度 int底层都是4个字节 --如何写入当前的时间 now() sysdate() current_date --时间的方式多样 '1256-12-23' "1256/12/23" --不区分单引号和双引号 --delete语句中from不可少 学号是主键 = 不能为空 + 唯一 email唯一 姓名不能为空 性别默认值是男 年龄范围18---30岁 需要指明相应的约束constraint */
2、修改和删除数据库表
-- 修改表的结构 -- 1.增加一列 alter table student add score double(4,1) ; -- 默认最后一列 insert into student (score ) value (123.4 ) insert into student (score ) value (123.4567 ) -- double float 不可以超越长度限制 alter table student add score double(4,1) first; alter table student add score double(4,1) after age; -- 可以使用after,不可以使用before -- 2.删除一列 alter table student drop score -- 3.修改一列 列名不变 alter table student modify score double(5,2); -- 3+2 -- 4.修改一列 列名改变 alter table student change score score2 double(5,2) -- 5.修改表名 alter table student rename to stu alter table stu rename student -- 查看数据 select * from student; select * from stu; -- 删除数据库表 drop table student;
3、表的完整性约束
为了防止不符合规范的数据存入数据库,在用户对数据进行插入、修改、删除等操作时,mysql数据库管理系统提供了一种机制来检查数据库中的数据是否满足规定的条件,以保证数据库中数据的准确性和一致性,这种机制就是约束。
约束从作用上可以分为两类:
(1) 表级约束:可以约束表中任意一个或多个字段。
(2) 列级约束:只能约束其所在的某一个字段。
(1)、主键约束
主键约束(primary key,缩写pk),是数据库中最重要的一种约束,其作用是约束表中的某个字段可以唯一标识一条记录。因此,使用主键约束可以快速查找表中的记录。就像人的身份证、学生的学号等等,设置为主键的字段取值不能重复(唯一),也不能为空(非空),否则无法唯一标识一条记录。
主键可以是单个字段,也可以是多个字段的组合。对于单字段主键的添加可以使用表级约束,也可以使用列级约束;而对于多字段主键的添加只能使用表级约束。
/*创建表student11*/ create table student11 ( stu_id int(10) primary key, stu_name varchar(3), stu_sex varchar (1) );
(2)、非空约束
非空约束(not null,缩写nk)规定了一张表中指定的某个字段的值不能为空(null)。设置了非空约束的字段,在插入的数据为null时,数据库会提示错误,导致数据无法插入。无论是单个字段还是多个字段非空约束的添加只能使用列级约束(非空约束无表级约束)。
-- 为已存在表中的字段添加非空约束 alter table student8 modify stu_sex varchar(1) not null; -- 使用alter table语句删除非空约束 alter table student8 modify stu_sex varchar(1) null;
(3)、 唯一约束
唯一约束(unique,缩写uk)比较简单,它规定了一张表中指定的某个字段的值不能重复,即这一字段的每个值都是唯一的。如果想要某个字段的值不重复,那么就可以为该字段添加为唯一约束。无论是单个字段还是多个字段唯一约束的添加均可以使用列级约束和表级约束。
(4)、 默认值约束
默认值约束(default)用来规定字段的默认值。如果某个被设置为default约束的字段没插入具体值,那么该字段的值将会被默认值填充。默认值约束的设置与非空约束一样,也只能使用列级约束。
(5)、字段值自动增加约束
自增约束(auto_increment)可以使表中某个字段的值自动增加。一张表中只能有一个自增长字段,并且该字段必须定义了约束(该约束可以是主键约束、唯一约束以及外键约束),如果自增字段没有定义约束,数据库则会提示“incorrect table definition; there can be only one auto column and it must be defined as a key”错误。
由于自增约束会自动生成唯一的id,所以自增约束通常会配合主键使用,并且只适用于整数类型。一般情况下,设置为自增约束字段的值会从1开始,每增加一条记录,该字段的值加1。
/*为student11表中的主键字段添加自增约束*/ alter table student11 modify stu_id int(10) auto_increment; -- 使用alter table语句删除自增约束 alter table studen11 modify stu_id int(10);
/* 建立一张用来存储学生信息的表 字段包含学号、姓名、性别,年龄、入学日期、班级,email等信息 学号是主键 = 不能为空 + 唯一 姓名不能为空 性别默认值是男 年龄范围18---30岁 email唯一*/ -- 问题 -- 1.同一个学生可以录入两次 学号唯一非空 主键冲突 -- 2.对年龄没有范围约束 -- 3.性别没有默认值 -- 4.姓名可以为空 -- 5.email没有唯一 create table student( sno int(4) primary key auto_increment, name varchar(12) not null, sex char(1) default '男', age int(3) , enterdate date, classname varchar(10), email varchar(20) unique ); insert into student values (10,'张三丰','男',25,'1234-12-23','武当一班','zhsf@wu.cn'); insert into student values (null,'张三丰',"m",52,'1234-12-23','武当一班','zhsf3@wu.cn'); insert into student (name,age)values ('张三丰',52); update student set sno = 50 where sno = 15 select * from student; -- 唯一约束 唯一 但可以为空 -- mysql支持检查约束,但是不起作用 -- 默认值是指不给该字段赋值的时候的默认值,并不是赋值null会编程默认值 -- 主键自增,并不是取最大值再加1 -- 主键 = 唯一+非空 -- 显示建表语句 show create table student; -- 删除表 drop table student; -- 给约束起一个名字 create table student( sno int(10), name varchar(12) not null, sex char(2) default '男', age int(2), enterdate date, classname varchar(20), email varchar(20), constraint pk_stu primary key(sno), -- constraint nk_stu_name name not null, -- constraint dk_stu_sex sex default '男', constraint uk_stu_email unique(email) ); alter table student drop primary key alter table student add primary key(sno) -- 删除唯一约束 alter table student drop index uk_stu_email -- 显示一个表的索引 show index from student; -- 非空约束只能在列后面定义 -- 默认值只能在列后面定义
4、表的外键约束
外键约束(foreign key,缩写fk)是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来,特别是针对修改或者删除的级联操作时,会保证数据的完整性。
外键是指表中某个字段的值依赖于另一张表中某个字段的值,而被依赖的字段必须具有主键约束或者唯一约束。被依赖的表我们通常称之为父表或者主表,设置外键约束的表称为子表或者从表。
举个例子:如果想要表示学生和班级的关系,首先要有学生表和班级表两张表,然后学生表中有个字段为stu_clazz(该字段表示学生所在的班级),而该字段的取值范围由班级表中的主键cla_no字段(该字段表示班级编号)的取值决定。那么班级表为主表,学生表为从表,且stu_clazz字段是学生表的外键。通过stu_clazz字段就建立了学生表和班级表的关系。
-- 创建一个班级表 create table class ( cno int(4) auto_increment, -- 只有主键才可以自增 cname varchar(12) not null, room varchar(4), primary key(cno) ); -- insert into class values (null,'java001',507); -- insert into class values (null,'java002',502); -- insert into class values (null,'大数据001',401); insert into class values (null,'java001',507),(null,'java002',502),(null,'大数据001',401); -- 查询数据 select * from class -- 创建一个学生表 drop table student create table student2( sno int(6) primary key auto_increment, name varchar(12), sex char(1), age int(2), classno int(4), constraint fk_stu2_classno foreign key (classno) references class(cno) ); desc student2; insert into student2 values(null,'zhangsan','男',23,1); insert into student2 values(null,'lisi','男',24,1); insert into student2 values(null,'wangwu','男',16,2); insert into student2 values(null,'zhaoliu','男',24,4); -- delete from class where cno = 1 update class set cno = 5 where cno = 2 select * from class select * from student2 -- 如果想删除1班,请手动的先对1班的学生进行处理(删除或者清空外键) update student2 set classno = null where classno = 1 delete from class where cno = 1 -- 希望在更新班级编号的时候,可以直接更新学生的班级编号;希望在删除某个班级的时候,清空学生的班级编号 -- 修改外键设置:外键要修改只能先删除再添加 alter table student2 drop foreign key fk_stu2_classno; -- 注意提示的问题,其实已经删除了外键 alter table student2 add constraint fk_stu2_classno foreign key (classno) references class(cno) on delete set null on update cascade /** 问题1:学生属于某个不存在的班级 问题2:删除了1个班级,学生的班级编号还存在 原因:没有进行语法上的约束:外键约束 foreign key 外键约束 --外键:一个表的某列的值要参照另外一个表(也可能是当前表)的主键列或者唯一列,该列成为外键 -- student2表的某列classno的值要参照另外一个表class(也可能是当前表)的主键列或者唯一列cno,该列成为外键 -- 如果参照当前表的主键列,也是外键,成为自关联 比如 emp表的mgr就是外键,参照emp的empno **/ drop table class; drop table student2 /** 外键的三种策略 1.不允许 要删除班级,先手动的对该班级的学生进行处理 如果无法确定,或者要保持灵活性 2.cascade 级联 留言和回复 朋友圈消息---点赞 评论 3.set null 清空外键 班级和学生 */
5、dml
select * from class select * from student -- 1.快速创建数据库表 结构和数据完全相同 create table class2 as select * from class select * from class2 -- 2.快速创建数据库表 只要结构相同,不要数据 create table class3 as select * from class where 1=2 select * from class3 -- 3.快速创建数据库表,只要部分结构 create table student3 as select sno,name,email from student -- where 1=2 select * from student3 -- 添加数据 insert into student3 values (5,'tianqi','tianqi@sxt.cn'),(6,'zhaoliu','zhaoliu@sxt.cn'), (7,'lisi','lisi@sxt.cn') insert into student3 set sno = 8,name = 'lisi2' ; -- 删除所有数据 delete from student3 truncate table student3 -- 删除所有数据时,更推荐使用该方式,效率高
五、事务
1、事务及其特征
(1)、事务的概念
事务(transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由dbms中的事务管理子系统负责事务的处理。
(2)、事务的特征
事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(atomicity)、一致性(consistency)、隔离(isolation)和持久性(durability)。这四个特性简称为acid特性。
a. 原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。
使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。
b. 一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。
c. 隔离性
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当a账户中的转出和b账户中转入操作都执行成功后才能看到a账户中的金额减少以及b账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。
d. 持久性
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
2、事务的操作
-- 创建account账户表 create table account( id int primary key auto_increment, username varchar(30) not null, balance double ); -- 为account账户表同时插入两条数据 insert into account (username, balance) values('张三', 2000),('李四', 2000); -- 查看account账户表中的数据 select * from account; -- 开启转账事务 start transaction; update account set balance=balance-200 where username='张三'; update account set balance=balance+200 where username='李四'; select * from account; -- 如果不提交,运行结果只是显示的缓存结果,数据库真正是还没有被修改的 -- 当我们关闭数据库重新打开后,张三和李四的账户余额并没有发生任何变化。 -- 这是因为当我们使用“start transaction”开启一个事务后,该事务的提交方式不再是自动的, -- 而是需要手动提交,而在这里,我们并没有使用事务提交语句commit, -- 所以对account表中数据的修改并没有永久的保存到数据库中,也就是说我们的转账事务并没有执行成功 -- 提交转账事务 commit; -- 事务的回滚也可以看做是结束事务的标记,但是回滚的事务并没有执行成功, -- 而是让数据库恢复到了执行事务操作前的初始状态。 -- 需要注意的是事务的回滚必须在事务提交之前,因为事务一旦提交就不能再进行回滚操作。 rollback;
3、事物的隔离级别
(1)、事务的并发问题
a.脏读(dirty read):当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
b.不可重复读(unrepeatableread):指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
c.幻读(phantom read):幻读与不可重复读类似。它发生在一个事务(t1)读取了几行数据,接着另一个并发事务(t2)插入了一些数据时。在随后的查询中,第一个事务(t1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
(2)、事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以mysql中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为read uncommitted、read committed、repeatable read以及serializable,隔离级别越低,越能支持高并发的数据库操作。
√:会出现问题 ×:不会出现问题,已解决问题 | |||
隔离级别 | 脏读 | 不可重复读 | 幻读 |
read uncommitted | √ | √ | √ |
read committed | × | √ | √ |
repeatable read | × | × | √ |
serializable | × | × | × |
-- 查看默认的事务隔离级别 mysql默认的是repeatable read select @@tx_isolation -- 设置事务的隔离级别 set session transaction isolation level read uncommitted; set session transaction isolation level read committed; set session transaction isolation level repeatable read; set session transaction isolation level serializable;
推荐阅读
-
Python实现mysql数据库更新表数据接口的功能
-
IIS下PHP连接数据库提示mysql undefined function mysql_connect()
-
从Web查询数据库之PHP与MySQL篇
-
PHP 获取MySQL数据库里所有表的实现代码
-
Django+mysql配置与简单操作数据库实例代码
-
使用wordpress的$wpdb类读mysql数据库做ajax时出现的问题该如何解决
-
关于MySQL自增ID的一些小问题总结
-
CentOS 6.2 安装 MySQL 5.7.28的教程(mysql 笔记)
-
一台linux主机启动多个MySQL数据库的方法
-
MySQL实现快速删除所有表而不删除数据库的方法