黑马就业班(01.JavaSE Java语言基础\12.MySQL数据库)——part3:多表查询&事务

1、多表查询
  多表查询就是从多张表中进行数据的查询,具体查询语法如下:

select
	列名列表
from
	表名列表(多个表)
where....

  为了对多表查询进行示例,我们先创建2个表

# 创建部门表
CREATE TABLE dept(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);
-- 部门表插入数据,id不需要插入,其会自动增长
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');

# 创建员工表
CREATE TABLE emp (
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(10),
	gender CHAR(1), -- 性别
	salary DOUBLE, -- 工资
	join_date DATE, -- 入职日期
	dept_id INT, -- 员工表外键列,用来关联部门表的id字段
	FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
);
-- 员工表插入数据,同样id不需要插入,自增
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);

-- 分别查询2个表
SELECT * FROM dept;
SELECT * FROM emp;

在这里插入图片描述
在这里插入图片描述
  我们执行下面语句进行多表查询

-- 多表查询
SELECT * FROM emp,dept;

在这里插入图片描述
  多表查询,查询出来的结果集我们称之为笛卡尔积。emp表有5行数据,而dept表有3行数据,因此他们的笛卡尔积是15。
  笛卡尔积:

* 有两个集合A,B .取这两个集合的所有组成情况。
* 要完成多表查询,需要消除无用的数据

  这15条数据里面有很多无用的数据,需要对其进行消除。只有“dept_id = id”的时候,数据才是有用的。
  按照当前的业务规则(此处是“dept_id = id”)消除无用的数据。在用多表查询并去除无用数据之前,我们先介绍一下多表查询的分类,通过这几类多表查询,我们可以消除无用的数据。

多表查询的分类

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`;

(1)
(2)
(3)查询结果与(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`;

  内连接查询需要注意的事项:内连接查询的其实就是表的交集部分

1. 从哪些表中查询数据
2. 条件是什么
3. 查询哪些字段

  内连接查询相应的查询代码如下:

-- ----------------------------------------------内连接查询
-- ---------隐式内连接查询
-- (1)既查询所有员工信息和对应的部门信息,并满足业务条件dept.id = emp.dept_id。这样就将多余的数据消除了。
SELECT * FROM emp,dept WHERE emp.dept_id = dept.id;

-- (2)查询员工表的名称,性别。部门表的名称,同样满足业务条件dept.id = emp.dept_id 以消除多余的数据
SELECT emp.`NAME`,emp.`gender`,dept.`name` FROM emp,dept WHERE dept.`id` = emp.`dept_id`;

-- (3)发现表名太长写起来很麻烦,我们可以在from字段给表起一个别名。
-- 另外我们将查询的正确格式写到下面,以后应该按照如下格式写代码
SELECT
	t1.`NAME`, -- 员工表的姓名
	t1.`gender`, -- 员工表的性别
	t2.`name` -- 部门表的名称
FROM
	emp AS t1,
	dept t2
WHERE
	t1.`dept_id` = t2.`id`; -- 最后注意加分号。如果我们某一句SQL语句无法运行,我们可以看看他的前一句是不是没有分号

-- ---------显式内连接查询
-- 语法: 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`; # 这两句的查询结果与前面(2)(3)查询相同

2、外连接查询:
  左外连接

* 语法:select 字段列表 from1 left [outer] join2 on 条件;
* 查询的是左表所有数据以及其交集部分。

  右外连接

* 语法:select 字段列表 from1 right [outer] join2 on 条件;
* 查询的是右表所有数据以及其交集部分。

  相应代码示例如下

-- 我们先查询emp表,然后给emp表添加一行数据(新员工),但是不指定这个新员工的dept_id(部门)
SELECT * FROM emp;

在这里插入图片描述

-- 需求:查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
-- 再和之前一样隐式内连接查询
SELECT 
	t1.* , t2.name
FROM 
	emp t1 , dept t2	
WHERE
	emp.`dept_id` = dept.`id`;

在这里插入图片描述
  我们发现员工表新添加的数据没有加进来,因为第六条新员工信息并没有指定部门id也就是“dept_id”,那么在2表共同查询的时候,这一条信息被认为是不合法的信息被排除了。

-- 为了完成我们需求,我们需要查询整个员工表,而不管里面的员工是否有部门id“dept_id”
-- 我们使用左外连接,查询整个左表所有数据以及其与右表交集部分。
-- 内连接查询的其实就是交集部分
SELECT
	t1.* , t2.`name`
FROM
	emp t1 LEFT OUTER JOIN dept t2
ON 
	t1.`dept_id` = t2.`id`;

在这里插入图片描述
  我们使用左外查询,不仅将2表交集全部查询到(内连接查询的其实就是交集部分),而且可以将左边emp表中没有dept_id的数据也查询出来。当然,使用右外查询的效果差不多,换一下位置即可。

-- 右外连接(效果与左外连接相同)
SELECT
	t1.* , t2.`name`
FROM 
	dept t2 RIGHT OUTER JOIN emp t1
ON
	t2.`id` = t1.`dept_id`;

3、子查询:
  概念:查询中嵌套查询,称嵌套查询为子查询。示例如下:

-- ----------------------------------------------子查询(员工表省略,就是上面使用的emp表)
-- 对于员工表emp,我们想要查询工资最高的员工信息
-- 1、查询员工表中的最高工资
SELECT MAX(salary) FROM emp; # 结果是9000
-- 2、查询员工信息,并且工资等于9000的
SELECT * FROM emp WHERE salary = 9000; # 结果查询到唐僧这一条目

-- 一条sql就完成这个操作。子查询
SELECT * FROM emp WHERE salary = (SELECT MAX(salary) FROM emp); # 同样查询到唐僧条目

  子查询不同情况

-- -----------------------------这里emp表与dept表同样省略
1. 子查询的结果是单行单列的:
	* 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
	* eg:
	-- 查询员工工资小于平均工资的人
	SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);

2. 子查询的结果是多行单列的:
	* 子查询可以作为条件,使用运算符in来判断
	-- 查询'财务部'和'市场部'所有的员工信息
/*
必须先在dept表中查询2个部门对应的id,而emp表中的dept_id等于emp表的id(外键)
因此通过dept表的dept_id属性可以查询对应的员工信息。
而dept_id是一行多列的,因此应该子查询使用in关键字
*/
SELECT id FROM dept WHERE NAME='财务部' OR NAME = '市场部'; #结果是2与3(结果是一列多行)
SELECT * FROM emp WHERE dept_id = 2 OR dept_id = 3; # 查询到对应的3条信息(条件是一列多行)
-- 子查询方法(一列多行,使用in关键字来表示一个字段对应多个值)
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME='财务部' OR NAME = '市场部'); #效果相同
-- 另一种形式
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME IN ('财务部' , '市场部'));

3. 子查询的结果是多行多列的:
	* 子查询可以作为一张虚拟表参与查询
	-- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
/*
这里我们需要查询部门信息与员工信息,那么就需要使用内连接查询查询2张表(emp与dept)的共同信息
但是这里员工表的信息又被限定,不需要查询整张emp,而要查询emp中满足条件的部分(也就是相应的信息我们无法直接从emp表获得,而必须通过查询emp表才能获得,那么可以将获得的数据封装为一张新表再进行2表之间的查询)
我们查询emp中满足条件的部分,结果又是一张新表,因此可以将新表作为一张虚拟表参与内连接查询
*/
-- 子查询+隐式内连接(这里将emp的条件先使用,查询一个虚拟的表出来参与查询)
SELECT * FROM dept t1, (SELECT * FROM emp WHERE join_date>'2011-11-11') t2 
	WHERE t1.id = t2.dept_id;  

-- 普通内连接(需要判断emp的条件与dept的条件,用AND连接)
SELECT * FROM dept t1 , emp t2 WHERE t1.`id` = t2.`dept_id` AND t2.`join_date`>'2011-11-11'

多表查询练习

  练习的相应代码如下:先创建几个表格

/*
数据库的设计理念:有4个表:员工表、部门表、职务表、工资等级表
一个员工对应一个部门、一个职务,一个工资等级,而另外三个元素则可以对应多个员工
这是一(3个)对多(员工)的关系,因此我们在员工表中创建2个外键指向部门表以及职务表
*/
-- 部门表
CREATE TABLE dept(
	id INT PRIMARY KEY ,
	dname VARCHAR(50),
	loc VARCHAR(50)
);
-- 添加4个部门
INSERT dept(id,dname,loc) VALUES 
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');

-- --------------------
-- 职务表,职务名称,职务描述
CREATE TABLE job(
	id INT PRIMARY KEY,
	jname VARCHAR(20),
	description VARCHAR(50)
);
-- 添加4个职务
INSERT INTO job(id , jname , description) VALUES
(1, '董事长', '管理整个公司,接单'),
(2, '经理', '管理部门员工'),
(3, '销售员', '向客人推销产品'),
(4, '文员', '使用办公软件');
-- --------------------------------
-- 员工表
CREATE TABLE emp (
  id INT PRIMARY KEY, -- 员工id
  ename VARCHAR(50), -- 员工姓名
  job_id INT, -- 职务id
  mgr INT , -- 上级领导
  joindate DATE, -- 入职日期
  salary DECIMAL(7,2), -- 工资
  bonus DECIMAL(7,2), -- 奖金
  dept_id INT, -- 所在部门编号
  
  -- 下面添加外键,分别是员工表job_id与职务表id对应的外键、员工表dept_id与部门表id对应的外键
  CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job(id),
  CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept(id)
);

-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);

-- -------------------------------------------------------------

-- 工资等级表
CREATE TABLE salarygrade (
  grade INT PRIMARY KEY,   -- 级别
  losalary INT,  -- 最低工资
  hisalary INT -- 最高工资
);

-- 添加5个工资等级
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);

  接下来是几个查询

-- ----------------------先整体查询4张表
SELECT * FROM dept;
SELECT * FROM emp;
SELECT * FROM job;
SELECT * FROM salarygrade;
-- -------------------------------------------练习
-- 需求:
				
-- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
/*
     分析:
	1.员工编号,员工姓名,工资,需要查询emp表  职务名称,职务描述 需要查询job表
	2.查询条件 emp.job_id = job.id
*/
-- 隐式内连接查询
SELECT
	emp.`id` , emp.`ename` , emp.`salary`,
	job.`jname` , job.`description`
FROM 
	emp , job
WHERE
	emp.`job_id` = job.`id`;

-- --------------------------------------------------
-- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
/*
	分析:
	1. 员工编号,员工姓名,工资 emp  职务名称,职务描述 job  部门名称,部门位置 dept
	2. 条件: emp.job_id = job.id and emp.dept_id = dept.id
*/
-- 隐式内连接查询(3个条件连在一起)
SELECT
	emp.`id` , emp.`ename` , emp.`salary`,
	job.`jname` , job.`description`,
	dept.`dname` , dept.`loc`
FROM 
	emp , job , dept -- 注意将3个表都写上
WHERE
	emp.`job_id` = job.`id` AND emp.`dept_id` = dept.`id`;
-- 注意这里员工表emp是中间表,因此在判断的时候应该以emp为中心(emp表设置了对应job表与的dept表的2个外键),
-- 判断emp的job_id与job的id相同,emp的dept_id与的dept的id相同

-- ----------------------------------------------------

-- 3.查询员工姓名,工资,工资等级
/*
	分析:
	1.员工姓名,工资 emp  工资等级 salarygrade
	-- 两张表没有关联,做前面那种等值的判断不合适。我们使用条件判断,使得emp的salary字段与salarygrade表的losalary 与hisalary 字段相关联,这样在查询salary表的grade的时候,就会依据当前查询的emp表的salary来查询
	2.条件 emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary
		emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
*/
/*
由于salarygrade表与emp表之间没有外键连接,那么我们想要判断工资等级,就必须通过emp的salary与salarygrade的losalary与hisalary判断,满足条件的才查询
*/
SELECT 
	t1.`ename`,
	t1.`salary`,
	t2.*
FROM
	emp t1,
	salarygrade t2
WHERE
	t1.`salary` >= t2.`losalary` AND t1.`salary` <= t2.`hisalary`;
	-- t1.`salary` between t2.`losalary` and t2.`hisalary`;
-- ----------------------------------------------------------------

-- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
/*
	分析:
	1. 员工姓名,工资 emp , 职务名称,职务描述 job 部门名称,部门位置,dept  工资等级 salarygrade
	2. 条件: emp.job_id = job.id and emp.dept_id = dept.id 
	and emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary			
*/
-- 其实就是上面几个查询的总结	
SELECT 
	t1.`ename`,
	t1.`salary`,
	t2.`jname`,
	t2.`description`,
	t3.`dname`,
	t3.`loc`,
	t4.`grade`	
FROM
	emp t1,
	job t2,
	dept t3,
	salarygrade t4
WHERE
	t1.`job_id` = t2.`id` AND
	t1.`dept_id` = t3.`id` AND
	t1.`salary` >= t4.`losalary` AND 
	t1.`salary` <= t4.`hisalary`;	
-- 要查询多个表的字段,必须对多个表之间的关系进行描述,每个表(job\dept\salarygrade)与主表(emp)之间必须通过某些公式相关联
-- ----------------------------------------------------------------
-- 5.查询出部门编号、部门名称、部门位置、部门人数				
/*
	分析:
	1.部门编号、部门名称、部门位置 dept 表。 部门人数 emp表
	-- 我们要查询这两个表的一些数据,那么就需要找到这两个表的关联。但是在这之前,我们发现部门人数不能直接在emp表里面找到,而必须通过对emp的dept_id字段进行分类查询才能获得部门人数。那么我们想到先使用分组查询,将查询出来的部门人数、dept_id再视为一张表,然后通过子查询,将这张表与dept表进行查询,这两张表之间的关联就是t1.`id` = t2.dept_id;
	-- 当两张表之间有关联,但是某些数据需要查询某张表之后才能获得,我们使用子查询,先查询这张表1获得想要的数据,将这些数据看做一张新表,新表再与另一张表2进行查询,并且可以使用最开始表1与表2之间的关联。
	
	2.使用分组查询。按照emp.dept_id完成分组,查询count(id)
	3.使用子查询将第2步的查询结果和dept表进行关联查询		
*/
SELECT
	t1.`id`,t1.`dname`,t1.`loc` , t2.total,t2.dept_id
FROM
	dept t1,
	(SELECT 
		dept_id,COUNT(id) AS total 
	FROM 
		emp 
	GROUP BY dept_id) t2
WHERE
	t1.`id` = t2.dept_id;
-- 这里注意,如果我们要查询的信息来自多个表,那么我们可以先子查询一个表,将某些信息封装为新的表
-- 然后再将新表与另外的表一起查询。需要注意的是,表与表之间必须要有某些关联

-- ---------------------------------------------------------------------------------
-- 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询				
/*
	分析:
	1.姓名 emp, 直接上级的姓名 emp
		* emp表的id 和 mgr 是自关联
	2.条件 emp.id = emp.mgr
	3.查询左表的所有数据,和 交集数据
		* 使用左外连接查询	
*/
/*
我们发现,一张emp表中涉及员工自己的id以及其直接上级的id,而且某一个员工的mgr对应的上级的id也在这个表中
这里实际上只涉及emp一张表,我们要查询员工姓名与其上级姓名,员工与上级通过id与mgr相联系,我们将emp分为2张表看
“员工表”——包含员工id、员工姓名,员工mgr;“上级表”——包含上级姓名、上级id(对应员工mgr),
当满足员工mgr = 上级id的时候,我们查询2张表中对应的信息
*/

SELECT 
	t1.`ename`,
	t1.`mgr`,
	t2.`id`,
	t2.`ename`
FROM 
	emp t1, -- 员工表
	emp t2 -- 上级表
WHERE
	t1.`mgr` = t2.`id`;
-- 我们发现在使用内连接查询过程中,由于内连接查询2个表的交集,因此会将没有上级的员工剔除,
-- 因此我们使用左外连接,这样就可以将员工表中上级为null的成员也查出
SELECT 
	t1.`ename`,
	t1.`mgr`,
	t2.`id`,
	t2.`ename`
FROM 
	emp t1 -- 员工表
LEFT OUTER JOIN
	emp t2 -- 上级表
ON
	t1.`mgr` = t2.`id`;
-- 当一个表中有2个字段的信息是同个涵义(自关联),我们在查询这两个字段相关其他字段的时候,
-- 可以将当前的表视为2个表,并且这两个表是自关联的。通过这两个字段(如“上级表”id与“员工表”mgr)	,
-- 我们可以查询到同一个表中这两个字段对应的信息(自己意会,很难言传)
SELECT * FROM dept;
SELECT * FROM emp;
SELECT * FROM job;
SELECT * FROM salarygrade;

  注意,当我们想使用某一个数据库的时候,这个地方必须选中要使用的数据库。在删除数据库的时候,如果删除不了,使用“drop database 数据库名称”删除。
在这里插入图片描述
  在看表与表之间的关系时,通过结构图来看更加清晰。
在这里插入图片描述
  需要注意的是视频在查询过程中的思路:先确定各类数据从哪一些表查询——然后确定查询的条件(查看结构图)——考虑使用哪一种方法查询(多表查询:内连接、外连接、子查询;单表查询:分页查询、排序查询、分组查询等等)。

事务

1、事务的基本介绍
  概念:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
  操作:

1. 开启事务: start transaction;
2. 回滚:rollback;
3. 提交:commit;

  例子

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;
-- 将所有account数据还原为1000
UPDATE account SET balance=1000; 
/*
需求:张三给李四转账 500 元
那么张三的balance-500与李四的balance+500必须同时执行成功,我们可以使用事务
*/
-- -------------------------------------------------------------------------------------
-- 开启事务
START TRANSACTION; -- 1

-- 张三账户
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 2
#出错了 -- 我们在这里故意制造一个错误,发现balance值出现问题,zhangsan减500,但是lisi没有加500
-- 李四账户
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 3

-- 提交事务,MySQL数据库中事务默认自动提交
COMMIT; -- 4
-- 如果事务出错,那么就进行回滚
ROLLBACK;  -- 5

/*
我们先开启事务,并且打开另一个SQLyog(这个用于观察当前表的真实状态,而不是临时状态)
我们先保持错误,执行1,2,3。这个时候没有提交也没有回滚,事务还没有结束。查询这个SQLyog发现临时数据balance出错,而查看另一个SQLyog发现数据没有变(真实数据)。
临时变化在窗口关闭再打开后,就会被取消。
临时数据出问题,我们就进行回滚,使得所有临时数据回滚到事务开启时的状态。
执行5,再查看发现数据恢复正常,查看另一个SQLyog界面也是正常的。
这种方式可以保证事务出错的时候,表内的真实的数据不会被改变(如另一个SQLyog显示的那样),而临时数据变化我们可以通过回滚将其恢复到事务开启时的正常状态,只有数据正常变化,我们才对事务进行提交,这样真正的数据(如另一个SQLyog显示的那样)才会发生变化。

接下来把错误去除,再执行1,2,3,查看发现balance正常(发生变化),这些也是临时数据,查看另一个SQLyog数据还是不变
只有执行4提交后,另一个SQLyog真正的数据才会发生变化。

其实开启另一个SQLyog是为了显示真正的表数据只有在事务提交后才会发生变化,而事务出错必须进行回滚,
使得数据恢复到事务开启的状态,等事务问题排除,再执行提交,这样真正的数据才会发生变化。
因此事务这种机制保证了整个过程的安全性
*/

  MySQL数据库中事务默认自动提交

-- 如果我们不开启事务,就相当于默认提交事务。如果我们开启了事务,就必须对事务进行手动提交。如果我们开启事务,但是没有提交,关闭窗口后事务操作的数据是不会发生变化的。(既事务默认自动回滚)(这部分看视频2里面怎么说,其实就是我们平常的增删改语句就是默认自动提交事务)
* 事务提交的两种方式:
	* 自动提交:
		* mysql就是自动提交的
		* 一条DML(增删改)语句会自动提交一次事务。
	* 手动提交:
		* Oracle 数据库默认是手动提交事务
		* 需要先开启事务,再提交
* 修改事务的默认提交方式:
	* 查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交  0 代表手动提交
	* 修改默认提交方式: set @@autocommit = 0;
	-- 修改提交方式影响的是我们普通增删改的动作,如果我们将aotocommit修改为0手动提交,那么我们不开启事务正常执行增删改操作,表的真实数据是不会发生变化的。只有提交后表的数据才发生变化,一般我们不修改默认自动提交(MySQL)。而Oracle数据库则需要手动提交。
	-- 对于事务来说,开启事务就必须手动提交,否则数据不会变化。

2、事务的四大特征(面试经常提及)
  如下:

1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3. 隔离性:多个事务之间。相互独立。
4. 一致性:事务操作前后,数据总量不变

  事务的原理见PDF-5.4

3、事务的隔离级别
  概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
  存在的问题

1. 脏读:一个事务,读取到另一个事务中没有提交的数据
2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
3. 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
-- MYSQL数据库看不到幻读,只能演示脏读与虚读(查看视频4)

  隔离级别

1. read uncommitted:读未提交
	* 产生的问题:脏读、不可重复读、幻读
2. read committed:读已提交 (Oracle)
	* 产生的问题:不可重复读、幻读
3. repeatable read:可重复读 (MySQL默认)
	* 产生的问题:幻读
4. serializable:串行化
	* 可以解决所有的问题

* 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
* 数据库查询隔离级别:
	* select @@tx_isolation;
* 数据库设置隔离级别:
	* set global transaction isolation level  级别字符串;
	设置的时候,只有将当前的连接断开再重新连接(关闭SQLyog再打开),这样隔离级别设置才会生效

  演示(查看视频,这一部分不是当前重点,查看视频演示即可)

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;

DCL

1、SQL分类
  SQL的分类如下

1. DDL:操作数据库和表
2. DML:增删改表中数据
3. DQL:查询表中数据
4. DCL:管理用户,授权

2、DBA:数据库管理员

3、DCL:管理用户,授权
3.1 管理用户
  我们必须进入mysql数据库,才能对用户进行修改。因为保存用户信息的表是mysql数据库的user表。
  添加用户:

* 语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
CREATE USER 'zhangsan'@'localhost' IDENTIFIED BY '123'; -- 本机可访问
CREATE USER 'lisi'@'%' IDENTIFIED BY '456'; -- 在任意电脑上都可以使用用户登录

在这里插入图片描述

  删除用户:

* 语法:DROP USER '用户名'@'主机名';
* DROP USER 'zhangsan'@'localhost'; -- 用户zhangsan被删除

在这里插入图片描述

  修改用户密码:

-- ---------------------修改密码(注意,修改用户密码之前,必须先使用mysql表,然后才能对user表进行修改)
-- 修改用户“lisi”的密码
-- UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'zhangsan';-- 这里PASSWORD函数用于加密密码
-- SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
SET PASSWORD FOR 'zhangsan'@'localhost' = PASSWORD('789');

-- 想要修改其他用户的密码,我们必须进入root用户的界面,才能看到系统的mysql数据库以及user表,才知道数据库结构,才能修改相应的密码。   
   * 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. 使用新密码登录。

  查询用户:用户数据存放在数据目录下的mysql数据库的user表中

-- 1. 切换到mysql数据库
USE mysql;
-- 2. 查询user表
SELECT * FROM USER;

* 通配符: % 表示可以在任意主机使用用户登录数据库
-- 我们发现查询出来2个用户,其实他们是同一个用户。第一个用户表示当前的用户root可以在当前的主机上登录;也可以在任意主机(“%”表示)上登录。

在这里插入图片描述

3.2 权限管理
  查询权限:

-- 查询权限
	SHOW GRANTS FOR '用户名'@'主机名';
	SHOW GRANTS FOR 'lisi'@'%';

  授予权限:

-- 授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';-- 这个用户只能操作我们指定的数据库的指定表。
-- 授予用户lisi查询权限
GRANT SELECT ON db1.account TO 'lisi'@'%';
-- 给张三用户授予所有权限,在任意数据库任意表上
GRANT ALL ON *.* TO 'zhangsan'@'localhost';

  撤销权限:

-- 撤销权限:
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值