mysql基础

一、 DQL(Data Query Language)语言的学习

1.1 基础查询

# 1.基础查询
/*
语法:
select 查询列表 from 表名;
特点:
1). 查询列表可以是:表中的字段、常量、表达式、函数
2). 查询的结果是一个虚拟的表格	
*/
USE myemployees;
# ①.查询单个字段
SELECT last_name FROM employees;
# ②.查询表中的多个字段
SELECT last_name,salary,email FROM employees;
# ③.查询表中的所有字段
SELECT * FROM employees;

# 2.查询时的细节补充
/*
1). 查询时要先打开库 use 库名;
2). yog中格式化快捷键为选中后按F12
*/

# 3.查询常量值
SELECT 100;
SELECT 'john';


# 4.查询表达式
SELECT 100*98;
# 5.查询函数
SELECT VERSION();

# 6.起别名
/*
1). 便于理解
如果要查询的字段有重名的情况,使用别名可以区分开来
*/
# 方式一:使用AS
SELECT	100%98 AS '结果';
SELECT last_name AS,first_name ASFROM employees;
# 方式二:省略AS
SELECT last_name 姓1,first_name 名1 FROM employees;

# 案例:查询salary,显示结果为out put
/*
当出现关键字时,可以使用着重号或者双引号包裹
*/
SELECT salary `out put` FROM employees;

# 7.去重 使用关键字DISTINCT
# 案例:查询员工表中涉及到的所有部门的编号(去重之后)
SELECT DISTINCT `department_id` FROM employees;

# 8.+号的作用
/*
	mysql中的+号只有运算符的功能
	①. select 100 + 90 :两个操作数都是数值型,则作加法运算
	②. select '123' + 90:其中一方为字符型,试图将字符数值转换为数值型,转换成功,则继续做加法运算,如果转换失败则将字符型数值转换成0
	③. select null + 10: 只要其中一方为null则结果肯定为null
*/
# 案例:查询员工名和姓连接成一个字段,并显示姓名
SELECT last_name+first_name AS 姓名 FROM employees;#错误:查不到结果
# 正确演示:使用concat
SELECT CONCAT('a','b','v') 结果;
SELECT CONCAT(last_name,first_name) 姓名 FROM employees;



/*
练习:
1). 显示表departments的结构,并查询其中的全部数据
2). 显示出表employees中的全部job_id(不能重复)
3). 显示出表employees的全部列,各个列之间用都好连接,列头显示OUT_PUT
*/
#显示表结构使用DESC
DESC departments;
SELECT * FROM departments;
SELECT DISTINCT job_id FROM employees;
SELECT CONCAT(`employee_id`,',',`first_name`,',',`last_name`,',',`email`,',',`phone_number`,',',`job_id`,',',`salary`,',',IFNULL(commission_pct,0),',',`department_id`,',',`hiredate`) OUT_PUT FROM employees
# 上一句sql代码查出结果有null的情况,这是因为其中有一个列中的数据weinull导致连接后变成了null,以下补充一个函数
# IFNULL()
SELECT IFNULL(commission_pct,0) 结果 FROM employees;

1.2 条件查询

# 条件查询
/*
语法:
	select 查询列表 from 表名 where 筛选条件
分类:
	一、按条件表达式筛选:>,<, = ,<>, >=, <=
	二、按逻辑表达式查询:and,or,not
	三、模糊查询:like,between and,in,is null
*/

# 1. 按条件表达式筛选
# 案例一:查询工资>12000的员工信息
SELECT CONCAT(last_name,first_name) 姓名,salary 工资 FROM employees WHERE salary > 12000;

# 案例二: 查询部门编号不等于90号员工名和部门编号
SELECT CONCAT(last_name,first_name) 姓名,department_id 部门编号 FROM employees WHERE department_id<>90;

# 2. 按逻辑表达式筛选
# 案例一:查询工资在10000到20000之间的员工名、工资以及奖金
SELECT CONCAT(last_name,first_name) 姓名, salary 工资,commission_pct 奖金 FROM employees WHERE salary>10000 AND salary < 20000;
# 案例二:查询部门编号不是90-100 之间,或者供着高于15000的员工信息
SELECT 
	CONCAT(last_name,first_name) 姓名
FROM
	employees
WHERE
	(job_id< 90 OR job_id > 100) OR salary > 15000;
	
# 模糊查询
#1. like
# 案例一:查询员工中包含字符a的员工信息
# %代表通配符,代表任意多个字符,_表示任意单个字符,特殊符号通过\进行转义,
# 转义字符可以使用自定义,如:'_$_%' ESCAPE '$'
SELECT first_name FROM employees WHERE first_name LIKE '%a%'

#2. between and
# 包含临界值,顺序为大于等于左边的值,小于等于右边的值
# 案例一:查询员工编号在100-120之间的信息
SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;


#3. in
# 判断某个字段的值是否属于in列表中的某一项
# in列表中的值类型必须是一致或者兼容
#案例一:查询员工的工种编号是IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号

# 正常操作
SELECT
	last_name,
	job_id
FROM 
	employees
WHERE
	job_id='IT_PROG' OR job_id='AD_VP' OR job_id='AD_PRES'  


# 使用in
SELECT
	last_name,
	job_id
FROM 
	employees
WHERE
	job_id IN ('IT_PROG','AD_VP','AD_PRES');  


# 4. is null
# is not null 与is null 相对应
# =或<>不能用于判断null值,
#案例一:查询没有奖金的员工名和奖金率

SELECT
	last_name,
	commission_pct
FROM 
	employees
WHERE
	commission_pct IS NULL;



## 补充:安全等于 :<=>

# 案例一:查询没有奖金的员工名和奖金率(安全等于判断null)
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE 
	commission_pct <=> NULL;
	
# 安全等于判断普通值
SELECT
	last_name,
	salary
FROM
	employees
WHERE 
	salary <=> 12000;
	
	
/*
练习:查询员工号为176的员工的姓名和部门号和年薪

*/
SELECT
	last_name,
	department_id,
	salary*12*(1 + IFNULL(commission_pct,0)) 年薪
FROM 
	employees
WHERE
	employee_id = 176;

1.3 排序查询

# 进阶3:排序查询

/*
语法:
	select 查询列表 from 表 [where 筛选条件] order by 排序列表 [asc|desc]
	
asc 代表升序,desc代表降序,
默认升序	
*/


# 案例一:查询员工信息,要求工资从高到底排序
# desc 降序

SELECT * FROM employees ORDER BY salary DESC;



# 案例二:查询部门编号>=90的员工信息,按入职时间的先后进行排序[添加了筛选条件]
SELECT 
	*
FROM
	employees
WHERE
	department_id >= 90
ORDER BY 
	hiredate ASC;
#案例三:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT
	last_name,salary*12*(1 + IFNULL(commission_pct,0)) 年薪
FROM 
	employees
ORDER BY
	salary*12*(1 + IFNULL(commission_pct,0)) DESC;
	
	
	
#案例四:按年薪的高低显示员工的信息和年薪【按别名排序】
SELECT
	last_name,salary*12*(1 + IFNULL(commission_pct,0)) 年薪
FROM 
	employees
ORDER BY
	年薪 DESC;

#案例五:按姓名的长度显示员工的姓名和工资【按函数排序】
SELECT
	LENGTH(last_name) 字节长度,last_name,salary
FROM 	
	employees
ORDER BY
	LENGTH(last_name) DESC;


#案例六:查询员工信息,要求先按工资升序,工资相同再按照员工编号降序【按多个字段排序】
SELECT
	* 
FROM 	
	employees
ORDER BY
	salary ASC,employee_id DESC;

1.4 常见函数和分组函数

# 进阶4:常见函数
/*

调用:
	select 函数名(实参列表) [from 表]
分类:
	1. 单行函数,如:concat、length、ifnull等
		单行函数分类:
			①. 字符函数
				1)length();获取参数值的字节个数
				2)concat(): 拼接字符串
				3)upper():  转大写
				4)lower():  转小写
				5)substr():
				6)substring():
				7)instr(string,substring): 返回字串在字符串中第一次出现的索引
				8)trim():  默认去掉字符串首尾的空格
				9)lpad(string1,num,string2):   用指定的string2字符填充string1的左边填充指定的num的长度
				10)rpad(string1,num,string2): 右填充
				11)replace(string,string1,string2):使用string2替换掉string中出现的string1
			②. 数学函数
				1)roud(): 四舍五入
				2)ceil():向上取证
				3)floor():向下取整
				4)truncate(被截断数,截断保留):截断小数点后面的数字,
				5)mod(除数,被除数): 取余
			③. 日期函数
				1)now():返回当前系统日期+时间
				2)curdate():返回当前系统日期,不包含时间
				3)curtime():返回当前系统时间,不包含日期
				4)获取指定的部分,年、月、日、小时、分钟、秒
				6)str_to_date():将字符串通过指定格式转换成日期str_to_date(9-12-1999','%m-%d-%Y') 1999-09-13
				7)date_format():将日期转换为字符 date_format('2018/6/6'.'%Y年%m月%d日') 2018年06月06日 
				8)datediff(date1,date2)检查date1和date2之间的天数
			④. 其他函数[补充 ]
				1)version(): 查看当前版本号
				2)database():查看当前库
				3)user():    查看当前库
			⑤. 流程控制函数[补充]
				1)if(表达式,value1,value2):if(10>5,'大','小'); 
				2)case:使用见下面的例子
	2. 分组函数,	
		功能:做统计使用,又称为统计函数、聚合函数、组函数
		1)sum():  求和
		2)avg():  平均值
		3)max():  最大值
		4)min():  最小值
		5)count():计算个数
	
	
*/
# 字符函数
# 1. length(),获取参数值的字节个数
SELECT LENGTH('周霖峰'); # 9
# 2. concat(),拼接字符串
# 3. upper、lower(),变大写和小写
SELECT UPPER('john');
SELECT LOWER('JoHn');
# 案例一:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名 FROM employees;
# 4. substr、substring(string,pos),截取字符串,sql语言中索引从1开始
# 截取指定索引处后面的所有字符
SELECT SUBSTR('李莫愁爱上了陆展元',7);
# 截取从指定索引出指定字符长度的字符
SELECT SUBSTR('李莫愁爱上了陆展元',1,3);


# 案例二:姓名中首字母大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) FROM employees;


# 5. instr(stirng,substring), 返回字串再字符串中的第一次出现的索引
SELECT
	INSTR('杨不悔爱上了殷六侠','殷六侠') AS out_put
FROM employees

# 6.trim(), 默认去掉首位空格

# 示例一:
SELECT 
	LENGTH(TRIM('   张翠山    ')) AS out_put;
# 示例二:去掉前后的a字符,使用参数: 要去掉的字符 from 字符串
SELECT	
	TRIM('a' FROM 'aaaaaaaaa张翠山aaaaaaaaaaa') AS out_put;
	
# 7. lpad(string1,num,string2): 用指定的string2字符填充string1的左边填充指定的num的长度
SELECT LPAD("殷素素",10,'+');
# 8. rpad(string1,num,string2): 实现右填充

# 9. replace(string,string1,string2):使用string2替换掉string中出现的string1
SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏');


/*
数学函数

*/

# 1. round(四舍五入的数据,保留的小数点后面的位数):四舍五入
SELECT ROUND(-1.65);
SELECT ROUND(1.768,2);

# 2. ceil()向上取整数

SELECT CEIL(2.42);

# 3. floor()向下取整
SELECT FLOOR(4.2);

# 4. truncate() 截断
SELECT TRUNCATE(1.492,1);

# 5. mod()取余
SELECT MOD(10,-3);

/*
日期函数

*/

# 1. now() 返回当前系统日期+时间
SELECT NOW();
# 2. curdate() 返回当前系统日期,不包含时间
SELECT CURDATE();

# 3. curtime() 返回当前时间,不包含日期
SELECT CURTIME();

# 4. 获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW());
SELECT YEAR('1998-1-1');
SELECT YEAR(hiredate)FROM employees;

SELECT MONTH(NOW());
SELECT MONTHNAME(NOW()); # 英文月份

SELECT DAY(NOW());
SELECT HOUR(NOW());
SELECT MINUTE(NOW())
SELECT SECOND(NOW());

# 5. str_to_date()将日期格式的字符串转换为指定格式的日期
# str_to_date(9-12-1999','%m-%d-%Y') 1999-09-13
SELECT STR_TO_DATE('9-13-1999','%m-%d-%Y');
# 6. date_format():将日期转换为字符
# date_format('2018/6/6'.'%Y年%m月%d日') 2018年06月06日 
SELECT DATE_FORMAT('2018/6/6','%Y年%m月%d日');

SELECT DATEDIFF(NOW(),'1998/7/16');

# 其他函数
SELECT DATABASE();


# 流程控制函数
# case
/*
case语法一:


case 要判断的字段或者表达式
when 常量1 then 要显示的值1或语句1
when 常量2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
end


*/

/*案例:查询员工的工资,要求:
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为为原工资
*/

SELECT 
	salary 原始工资,department_id,
	CASE department_id
	WHEN 30 THEN salary*1.1
	WHEN 40 THEN salary*1.2
	WHEN 50 THEN salary*1.3
	ELSE salary
	END AS 新工资
FROM 
	employees;

/*
case语法二:

case 
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
end

*/

# 案例:查询员工的工资情况
/*
	如果工资>20000,显示A级别
	如果工资>15000,显示B级别
	如果工资>10000,显示C级别
	否则,显示D级别
*/
SELECT 
	salary,
	CASE
	WHEN salary>20000 THEN 'A'
	WHEN salary>15000 THEN 'B'
	WHEN salary>10000 THEN 'C'
	ELSE 'D'
	END 级别
FROM employees;




##### 分组函数

# 1. 简单的使用
SELECT SUM(salary) 总和 FROM employees;
SELECT AVG(salary) 平均 FROM employees;
SELECT MIN(salary) 最小 FROM employees;
SELECT MAX(salary) 最大 FROM employees;
SELECT COUNT(salary) 总数 FROM employees;

SELECT SUM(salary),ROUND(AVG(salary),2),MIN(salary),MAX(salary),COUNT(salary) FROM employees;

# 2. 分组函数特点
1)sun、avg一般用于处理数值型
2)max、min、count可以处理任何类型
3)分组函数都忽略null4)可以和distinct搭配实现去重的运算



# 3. 和distinct搭配
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;

# 4. count函数的具体介绍
SELECT COUNT(salary) FROM employees;
# 以下代表查询表中的总行数
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;# 这里的1无论填什么都行,相当于在表中添加1列,每个数都是1,统计所有1的个数
# 一般使用count(*)

# 5. 分组函数使用注意事项 
/*
	同分组函数一同查询的字段要求是group by后的字段
*/

 

1.5 分组查询

# 进阶5:分组查询
/*
 引入:求出各部门的平均工资
 语法:
 
	select column,group_function(column)
	from 表名
	[where 条件]
	[group by 表达式]
	[having 条件]
	[order by column]
	
*/

# 案例1:查询每个工种的最高工资
SELECT 
	MAX(salary),job_id
FROM
	employees
GROUP BY 
	job_id;
	
# 案例2:查询每个位置上的部门个数
SELECT 
	COUNT(*),location_id
FROM 
	departments
GROUP BY location_id;


# 添加筛选条件
# 案例3:查询邮箱中包含a字符的,每个部门的平均工资
SELECT
	AVG(salary),department_id	
FROM 
	employees
WHERE
	email LIKE '%a%'
GROUP BY 
	department_id


# 案例4. 查询有奖金的每个领导手下的员工的最高工资
SELECT	
	MAX(salary),manager_id
FROM 
	employees
WHERE
	commission_pct IS NOT NULL
GROUP BY 
	manager_id; 


# 复杂的筛选条件
# 案例5:查询哪个部门的员工个数>2

/*
having 添加分组后的筛选
*/
SELECT 
	department_id 部门,COUNT(*)
FROM
	employees

GROUP BY 
	department_id

HAVING COUNT(*)>2;


# 案例6:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT 
	MAX(salary),job_id
FROM 
	employees
WHERE
	commission_pct IS NOT NULL
GROUP BY 
	job_id
HAVING
	MAX(salary)>12000;
	
	
	
# 案例7:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪一个,以及其最低工资

SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;



# 分组查询按表达式或函数分组
# 案例:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些

SELECT
	COUNT(*)
FROM 
	employees

GROUP BY 
	LENGTH(CONCAT(last_name,first_name))
HAVING
	COUNT(*)>5;

## 按多个字段分组
# 案例:查询每个部门每个工种的员工的平均工资

SELECT AVG(salary),`department_id`,job_id
FROM employees
GROUP BY `department_id`,job_id;
 

 
# 添加排序
## 案例:查询每个部门每个工种的员工的平均工资,并且按平局工资的高低显示


SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id
ORDER BY AVG(salary) DESC;


1.6 连接查询

USE myemployees;
SELECT DATABASE();

## 进阶6: 连接查询(多表查询)
/*

用处:当查询来自多个表的时候,就会用到连接查询
笛卡尔乘积现象;表1有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件 


连接查询分类
	按年代分类
		sql 192标准:仅仅支持内连接
		sql 199标准【推荐】:支持内连接、外连接(左外和右外)、交叉连接
	按功能分类
		内连接
			等值连接
			非等值连接
			自连接
		外连接
			左外连接
			右外连接
			全外连接
		交叉连接
*/

# 出现笛卡尔乘积
SELECT `name`,boyName
FROM beauty,boys;
# 添加有效的连接条件
/*需要对连接条件中的id值进行表名的限定*/
SELECT `name`,boyName
FROM beauty,boys
WHERE beauty.`boyfriend_id` = boys.`id`;


# sql92标准
# 等值连接
/*
①.多表等值连接的结果为多表的交集部分
②.n表连接,至少需要n-1个连接条件
③.多表的顺序没有要求
④.一般需要为表取别名
⑤.可以搭配前面介绍的所有子句使用,比如牌组、分组、筛选
*/
# 案例1:查询女生名和对应的男神名
SELECT `name`,boyName
FROM beauty,boys
WHERE beauty.boyfriend_id=boys.`id`
# 案例2:查询员工名和对应的部门名
USE myemployees;
SELECT	last_name,department_name
FROM departments,employees
WHERE employees.`department_id` = departments.`department_id`;

#为表取别名
/*如果为表取了别名,则查询的字段就不能使用原来的表名进行限定*/
# 案例3:查询员工名、工种名、工种号
SELECT e.last_name,e.job_id,j.job_title
FROM employees e,jobs j
WHERE e.`job_id` = j.`job_id`;

# 添加筛选条件
# 案例4:查询有奖金的员工名、部门名
SELECT last_name,department_name,commission_pct
FROM employees,departments
WHERE employees.`department_id`=departments.`department_id` AND employees.`commission_pct` IS NOT NULL;

# 案例5:查询城市名中第二个字符为o的部门名和城市名
SELECT departments.`department_name`,locations.`city`
FROM departments,locations
WHERE departments.`location_id`=locations.`location_id` AND locations.`city` LIKE '_o%';

# 添加分组
#案例6: 查询每个城市的部门个数
SELECT COUNT(*) 部门个数,city
FROM departments d,locations l
WHERE d.`location_id` = l.`location_id`
GROUP BY l.`city`;


# 添加排序
# 案例7:查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT job_title,COUNT(*) 员工个数
FROM employees e, jobs j
WHERE j.`job_id`=e.`job_id`
GROUP BY j.`job_title`
ORDER BY COUNT(*) DESC;


# 实现三表连接
# 案例8:查询员工名、部门名和所在城市
SELECT last_name,department_name,city
FROM departments d,employees e,locations l
WHERE	e.`department_id`=d.`department_id` AND d.`location_id` = l.`location_id`;


/*
不等值查询
*/
CREATE TABLE job_grades
(grade_level VARCHAR(3),
 lowest_sal  INT,
 highest_sal INT);

INSERT INTO job_grades
VALUES ('A', 1000, 2999);

INSERT INTO job_grades
VALUES ('B', 3000, 5999);

INSERT INTO job_grades
VALUES('C', 6000, 9999);

INSERT INTO job_grades
VALUES('D', 10000, 14999);

INSERT INTO job_grades
VALUES('E', 15000, 24999);

INSERT INTO job_grades
VALUES('F', 25000, 40000);

## 案例1:查询员工工资和工资级别
SELECT	salary,grade_level
FROM employees e,job_grades g
WHERE e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal` AND g.`grade_level` = 'A';

/*
自连接

*/

# 案例1:查询员工名和上级的名称
SELECT e1.last_name 员工名称,e2.`last_name` 上级名称
FROM employees e1, employees e2
WHERE e1.`manager_id` = e2.`employee_id`;


/*sql 199语法

语法:
	select 查询列表
	from 表1 别名 【连接类型】
	join 表2 别名 
	on 连接条件
	where 筛选条件
	[group by ]
	[having]
	[order by]
分类
内连接:(⭐)inner
外连接
	左外:(⭐)left [outer]
	右外:(⭐)right [outer]
	全外:full [outer]
交叉连接:cross 
*/


/*
内连接
select 查询列表
from 表1 别名 
inner join 表2 别名
on 连接条件;
*/

# 等值连接
/*
特点
1)添加排序、分组、筛选
2)inner可以省略
3)筛选条件放在where后面,连接条件放在on后面,提高 分离性
*/
# 案例1: 查询员工名、部门名
 SELECT last_name,department_name
 FROM employees e
 INNER JOIN departments d
 ON d.`department_id` = e.`department_id`;
 
 # 案例2:查询名字中包含e的员工名和工种名
 SELECT last_name,job_title
 FROM employees e
 INNER JOIN jobs j
 ON e.`job_id` = j.`job_id`
 WHERE last_name LIKE '%e%';



# 案例3:查询部门个数>3的城市名和部门个数
SELECT city,COUNT(*)
FROM departments d
INNER JOIN locations l
ON l.`location_id` = d.`location_id` 
GROUP BY l.`city`
HAVING COUNT(*) > 3;

# 案例4:查询部门的部门员工个数>3的部门名和员工个数,并按个数降序
SELECT department_name,COUNT(*)
FROM departments d
INNER JOIN employees e
ON e.`department_id` = d.`department_id`
GROUP BY d.`department_name`
HAVING COUNT(*)>3
ORDER BY COUNT(*) DESC;

# 案例5:查询员工名、部门名、工种名、并按照部门名降序

SELECT last_name,department_name,job_title
FROM employees e
INNER JOIN departments d
ON e.`department_id` = d.`department_id`
INNER JOIN jobs j
ON e.`job_id` = e.`job_id`
ORDER BY d.`department_name` DESC;

# sql 199 非等值连接

# 案例:查询员工的工资级别

SELECT last_name,salary,grade_level
FROM employees e
JOIN job_grades j
ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal`;


# 自连接
# 案例:查询员工的名字、上级的名字
SELECT e.`last_name` 员工的名称,m.`last_name` 管理者名称
FROM employees e
JOIN employees m
ON e.`manager_id` = m.`employee_id`## ⭐外连接

/*特点:
1.外连接的查询结果为主表中多有的记录
	如果从表中有和它匹配的,则显示匹配的值
	如果从表中没有和它匹配的,则显示null
	外连接查询结果=内连接结果+主表中有而从表中没有的记录
2. 左外连接,left join 左边的是主表
   右外连接,right join右边的是主表
3. 左外和右外交换两个表的顺序,可以实现同样的效果

*/
# 引入:查询男朋友不在男生表的女生名
SELECT `name`,boyName
FROM beauty g
LEFT OUTER JOIN boys b
ON g.`boyfriend_id` = b.`id`
WHERE boyName IS NULL;

# 查询哪个部门没有员工
USE myemployees;
SELECT department_name,last_name
FROM employees e
RIGHT OUTER JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`employee_id` IS NULL;


# 全外连接:mysql不支持
# 以下仅仅作展示
USE girls;
SELECT b.*,bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.boyfriend_id = bo.id;


# 交叉连接(笛卡尔乘积)
SELECT b.*,bo.*
FROM beauty b
CROSS JOIN boys bo;

1.7 子查询

/*
进阶7:子查询
含义:出现在其他语句中的select语句,称为子查询或内查询

外部的查询语句称为主查询或外查询
示例:
	select firt_name from employees where 
	department_id in (
		select department_id from departments
		where location_id = 1700
	)


分类:
按子查询出现的位置
	1. select后面(仅仅支持标量子查询)
	2. from后面(支持表子查询)
	3. where或having后面※(支持标量子查询※,列子查询※,行子查询)
	4. exists后面(相关子查询,表子查询)
按结果集的行列数不同:
	1. 标量子查询(结果集只有一行一列)
	2. 列子查询(结果集只有一列多行)
	3. 行子查询(结果集有一行多列)
	4. 表子查询(结果集一般为多行多列)
*/

# 一、where或having后面
/*
1. 标量子查询(单行子查询)
2. 列子查询(多行子查询)
3. 行子查询(多列多行)

特点:
1). 子查询放在小括号内
2). 子查询一般放在条件的右侧
3). 标量子查询,一般搭配这单行操作符使用(> < >= <= <>)
列子查询,一般搭配这多行操作符使用
IN、ANY/SOME、ALL

*/
# 1. 标量子查询
# 案例1:谁的工资比Abel高
SELECT last_name,salary
FROM employees
WHERE salary>(SELECT salary FROM employees WHERE last_name='Abel');

# 案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资

SELECT last_name,job_id,salary
FROM employees e
WHERE job_id = (SELECT job_id FROM employees WHERE employee_id = '141')
AND salary > (SELECT salary FROM employees WHERE employee_id = '143')

# 2.列子查询(多行子查询)

/*
多行操作符
in / not in  等于列表中的任意一个
any / some   和子查询返回的某一个值比较
all          和子查询返回的所有值比较 
*/


# 案例1 :返回location_id是1400或1700的部门中的所有员工姓名
SELECT * FROM departments;

SELECT last_name 
FROM employees 
WHERE department_id IN (SELECT department_id FROM departments WHERE location_id IN (1400,1700)) 

# 案例2:返回其它部门中比job_id为‘IT_PROG’部门任一工资低的员工的:工号,姓名,job_id以工资

SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ANY((SELECT salary FROM employees WHERE job_id='IT_PROG'))
AND job_id <> 'IT_PROG';


# 行子查询(结果集一行多列)
# 案例1:查询员工编号最小并且工资最高的员工信息

SELECT * 
FROM employees 
WHERE (employee_id,salary)=(
	SELECT MIN(employee_id),MAX(salary)
	FROM employees
)


# select后面的子查询  

# 案例1:查询每个部门的员工个数
SELECT d.*,(
	SELECT COUNT(*) FROM employees e
	WHERE e.`department_id` = d.department_id
) 员工个数
FROM departments d

# 案例2 :查询员工号=102的部门名
SELECT(
SELECT department_name
FROM departments d
INNER JOIN employees e
ON e.department_id = d.department_id
WHERE employee_id = '102');


# 三、from 后面的子查询使用

# 查询每个部门的平均工资的工资等级

SELECT grade_level,s.avg
FROM (
	SELECT AVG(salary) `avg`
	FROM employees
	GROUP BY department_id
) s
INNER JOIN job_grades g
ON s.avg BETWEEN g.`lowest_sal` AND g.`highest_sal`;


# 四、exists后面(相关子查询)

/*
语法:
exists(完整的查询语句)
结果
1或0
1 :存在
0 :不存在
*/

SELECT EXISTS (SELECT employee_id FROM employees WHERE salary = 30000); # 存在结果,1

# 案例1:查询有员工的部门名
SELECT department_name
FROM departments d
WHERE EXISTS(
	SELECT * 
	FROM employees e
	WHERE d.`department_id` = e.`department_id`
);

# 查询没有女朋友的男生信息
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
	SELECT boyfriend_id
	FROM beauty b
	WHERE bo.`id` = b.`boyfriend_id`
)


1.8 分页查询

# 分页查询
/*
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
语法:
	select 查询列表
	from 表
	[join type]
	[on 连接条件]
	[where 筛选条件]
	[group by 分组字段]
	[having 分组后的筛选]
	[order by 排序的字段]
	limit offset,size;
	
	[
		offset 表示要显示条目的起始索引,起始索引从0开始
		size表示要显示的条目个数
		
		
		公式:
		要显示的页数page,每页显示的条目数size
		
		limit (page-1)*size,size;
	]
	

*/



# 案例1:查询前5条员工信息
SELECT *
FROM employees
LIMIT 0,5;

# 查询第11条-第25条
SELECT *
FROM employees
LIMIT 10,15;

# 案例2 : 有奖金的员工信息,并且工资较高的前10名显示出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC 
LIMIT 0,10;



1.9 union联合查询

# 进阶9 :联合查询
/*
union:联合 合并:将多条查询语句的结果合并成一个结果

语法:
	查询语句1
	union
	查询语句2
	union 
	...
	
特点:
	要求多条查询语句的列数是一致的
	要求多条查询语句的查询的每一列的类型和顺序最好一致
	union关键字默认去重,如果使用union all 可以包含重复项
*/


# 引入案例:查询部门编号》90或者邮箱包含a的员工信息\
# 常规操作
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id > 90;

# 使用union
SELECT * FROM employees WHERE email LIKE '%a%' 
UNION 
SELECT * FROM employees WHERE department_id > 90;

二、 DML(Data Manipulation Language)语言的学习

2.1 插入语句

# DML语言
/*
数据操作语言
插入:insert 
修改:update
删除:delete

*/


# 插入语句
/*
语法:
方式一:
insert into 表名(列名,...) values(值1,...)

方式二:
语法:
inser into 表名
set 列名=值,列名=值,...


要求:
1. 插入的值的类型要与列的类型一致或兼容

*/

USE girls;
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id) 
VALUES(13,'唐艺昕','女','1990-4-23','1156465454',NULL,2)

SELECT * FROM beauty;

# 为空的内容可以不写,即列名和值都不写,要么都写,值为空null

# 列的顺序可以颠倒

# 可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致
INSERT INTO beauty 
VALUES(18,'张飞','男',NULL,'1922626',NULL,NULL)

# 方式二插入
INSERT INTO beauty
SET id=19,NAME='刘涛',sex='女',phone='999';

/*
两种方式区别:
1. 方式一支持插入多行,方式二不支持
insert into 表名(列名,...) values(值1,...),(值1,...)
2. 方式一支持子查询,方式二不支持

*/
# insert into 使用子查询
INSERT INTO beauty(id,NAME,sex,borndate,phone)
SELECT 26,'宋茜','女','1994-08-07','11878327';









2.2 修改语句

# # 二、修改语句

/*
1. 修改单表的记录※

语法:
	update 表名 set 列=新值,列=新值,... where 筛选条件;

2. 修改多表的记录【补充】
语法:
sql 192语法:
	update 表1 别名,表2 别名
	set 列=值,...
	where 连接条件
	and 筛选条件
sql 199语法:
	update 表1 别名
	inner|left|right join 表2 别名
	on 连接条件
	set 列=值,...
	where 筛选条件;


*/


# (一)、修改单表的记录
# 案例1:修改beauty中姓唐的女神的电话为16161555
UPDATE beauty SET phone='16161555' WHERE `name` LIKE '唐%';
SELECT * FROM beauty;

# (二)、修改多表的记录
# 案例1:修改张无忌女朋友的手机号为114

UPDATE beauty b
INNER JOIN boys bo
ON bo.`id` = b.`boyfriend_id`
SET b.`phone` = '116'
WHERE bo.`boyName` = '张无忌'

# 案例2:修改没有男朋友的女神的男朋友编号为2

UPDATE beauty b
LEFT OUTER JOIN boys bo
ON  bo.`id` = b.`boyfriend_id`
SET b.`boyfriend_id` = 2
WHERE bo.`id` IS NULL

2.3 删除语句

#三、删除语句
/*
方式一:delete
语法:
1.单表的删除※
delete from 表名 where 筛选条件
2.多表的删除
sql 192语法:
	delete 表1的别名,表2的别名
	from 表1 别名,表2 别名
	where 连接条件
	and 筛选条件

sql 199语法:
	delete 表1的别名,表2的别名
	from 表1 别名
	inner|left|right join 表2 别名
	on 连接条件
	where 筛选条件
【注意】: 这里表1的别名和表2的别名代表你要删除哪个表中的数据,如果是只删除
表1中的数据,delete后面只写表1的别名,表1的别名和表2的别名同时写代表两个一起级联删除。	
	



方式二:truncate
语法:
truncate table 表名
*/

# 方式一 delete
# 1.单表的删除
# 案例:删除手机号以9结尾的女神信息

DELETE FROM beauty WHERE phone LIKE '%9';

# 2. 多表的删除
# 案例:删除张无忌的女朋友的信息

DELETE b, 
FROM beauty b
INNER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`boyName` = '张无忌';


# 2. 多表的删除
# 案例:删除张无忌及其女朋友的信息
DELETE b,bo
FROM beauty b
INNER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`boyName` = '张无忌';

# 删除方式二 truncate
# truncate 语句不能加where,会直接删除表

TRUNCATE TABLE boys;

DELETE FROM boys;
/*
delete 和 truncate区别
1. truncate 效率更高
2. delete能加where条件,truncate 不能
3. 如果表中有自增长列,delete删除后,再插入数据,自增长的值从断点开始,而truncate自增长列的值从1开始。
4. truncate 删除没有返回值,delete删除有返回值
5. truncate删除不能回滚,delete删除可以回滚
*/ 

三、DDL(Data Define Language)语言的学习

3.1 库和表的管理

# DDL语言
/*
数据定义语言
一、库的管理
创建、修改、删除
二、表的管理
创建、修改、删除

创建:create
修改:alter
删除:drop

*/


# 库的管理
/*

*/


# 1. 库的创建
/*
语法:create database [if not exists] 库名;
*/

CREATE DATABASE IF NOT EXISTS books;



# 2. 更改库的字符集
ALTER DATABASE books CHARACTER SET gbk;

# 3. 库的删除
DROP DATABASE IF EXISTS books; 

# 二、 表的创建

/*
语法
create table 表名(
	列名 列的类型 [(长度) 约束],
	列名 列的类型 [(长度) 约束],
	...
	列名 列的类型 [(长度) 约束]
)

*/

# 1. 创建表book

CREATE TABLE book(
	id INT, #编号
	bName VARCHAR(20), #图书名
	price DOUBLE,#价格
	authorId INT,#作者编号
	publishDate DATETIME#出版日期
	
)

DESC book;



# 创建author表

CREATE TABLE author(
	id INT,
	au_name VARCHAR(20),
	nation VARCHAR(10)
)

DESC author


# 表的修改
# 1. 修改列名
ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME;
DESC book;
# 2. 修改列的类型或约束
ALTER TABLE book MODIFY COLUMN pubDate TIMESTAMP;
# 3. 添加新列
ALTER TABLE book ADD COLUMN annual DOUBLE;
# 4. 删除列
ALTER TABLE book DROP COLUMN annual;

# 5. 修改表名
ALTER TABLE author RENAME TO book_author;
DESC book_author

SHOW TABLES;



# 三、表的删除

DROP TABLE IF EXISTS book_author;
SHOW TABLES;


# 通用写法
DROP DATABASE IF EXISTS 旧库名;
CREATE DATABASE 新的库名;

DROP TABLE IF EXISTS 旧表名;
CREATE TABLE 表名();



# 表的复制
INSERT INTO author VALUES
(1,'123','riben'),
(2,'hfkajhf','zhongguo'),
(3,'fengtang','zhongguo'),
(4,'jinyong','zhongguo');


# 仅仅复制表的结构
CREATE TABLE copy LIKE author;
# 复制表的结构外加数据
DROP TABLE copy2;
CREATE TABLE copy2 SELECT * FROM author;


# 只复制部分数据
CREATE TABLE copy3
SELECT id,au_name
FROM author
WHERE nation='zhongguo';

# 仅仅复制部分结构
CREATE TABLE copy4
SELECT id,au_name
FROM author
WHERE 1=2;

3.2 常见数据类型介绍

# 常见数据类型
/*
数值型:
	整型
	小数:
		定点数
		浮点数
字符型:
	较短的文本:char、varchar
	较长的文本:text、blob(较长的二进制数据)
日期型:

*/

# 整型
/*
整数类型	字节		范围
Tinyint		1		有符号\无符号
Smallint 	2		
Mediumint	3		
Int、integer	4	
Bigint		8


设置长度代表的是最大宽度,如果不够会用0在左边填充,必须搭配zerofill使用!


*/

# 1. 如何设置无符号和有符号
CREATE TABLE tabl_int(
	t1 INT,
	t2 INT UNSIGNED
)

INSERT INTO tabl_int VALUES(-3131,3321);
SELECT * FROM tabl_int;



/*
小数:
浮点数类型	字节	范围
float(M,D)		4
double(M,D)		8
定点数类型	字节
DEC(M,D)	M+2
DECIMAL(M,D)	


# 特点:
1. M和D
D:小数点后的位数
M:整数部位外加小数部位的总长度

2. M和D都可以省略
如果是decimal,则M默认为10,D默认为0
如果是float和double,则会根据插入的数值的精度来决定精度

3. 定点型的精度较高,如果要求插入数值的精度较高用dicimal
*/


CREATE TABLE tab_float(
	f1 FLOAT(5,2),
	f2 DOUBLE(5,2),
	f3 DECIMAL(5,2)
);

SELECT * FROM tab_float;
INSERT INTO tab_float VALUES(123.45,123.45,123.45)



/*
字符型:
较短的文本:char,varchar
较长的文本:text,blob(较大的二进制)



字符串类型	最多字符数	描述及存储需求
char(M)		M		M为0~255之间的整数
varchar(M)	M		M为0~65535之间的整数


char 长度M可以省略 是固定长度的字符 空间耗费 效率高
varchar 长度M不可省略 是可变长度的字符 空间比较节省 效率低


*/

/*
Enum 枚举 只能是枚举中的一个  enum('a','b','c')

Set 集合 可以是set集合中的多个  set('a','b','c')
*/


/*
日期型


日期和时间类型	字节	最小值			最大值
date		4	1000-01-01		9999-12-31
datetime	8	1000-01-01 00:00:00	9999-12-31 23:59:59
timestamp	4	19700101080001		2038年的某个时刻(受时区影响)
time		3	-838:59:59		838:59:59
year		1	1901			2155
*/

3.3 常见约束

#常见约束
/*
含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确性和可靠性

分类:六大约束
	1. NOT NULl :非空,用于保障该字段的值不能为空
	2. DEFAULT  :默认,用于保证该字段有默认值
	3. PRIMARY KEY:主键,用于保证该字段的值的唯一性,并且非空
	4. UNIQUE:  :唯一,用于保障该字段的值具有唯一性,可以为空
	5. CHECK    :检查约束【mysql中不支持】
	6. FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自与主表的关联列的值


主键和唯一的大对比:

	保证唯一性	是否允许为空	一个表中可以有多少个	是否允许组合
主键	√		×			至多一个	是,但不推荐
唯一	√		√			可以有多个	是,但不推荐

组合使用 primary key(字段名1,字段名2)
	 unique(字段名1,字段名2)
	 
外键的特点:
		1. 要求在从表设置外键关系
		2. 从表的外键的类型和主表的关联列的类型要求一致或兼容
		3. 主表的关联列必须是一个key(一般是主键或唯一)
		4.插入数据时,先插入主表,再插入从表,删除数据时,先删除从表,在删除主表

添加约束的时机:
	1. 创建表时
	2. 修改表时


约束添加的分类
	1. 列级约束 :六大约束语法上都支持,但外键约束没有效果
	2. 表级约束:除了非空,默认,其他的都支持

CREATE TABLE 表名(
	字段名 字段类型 列级约束
	...
	表级约束
	
)

*/


# 创建表时添加约束
# 添加列级约束

CREATE DATABASE students;


USE students;


CREATE TABLE stuinfo(
	id INT PRIMARY KEY,
	stuName VARCHAR(20) NOT NULL,
	gender CHAR(1) CHECK(gender='男' OR gender='女'),# mysql不支持 check约束
	seat INT UNIQUE,
	age INT DEFAULT 18,
	majorId INT  REFERENCES major(id)# 外键约束列级约束不支持
)

CREATE TABLE major(
	id INT PRIMARY KEY,
	majorName VARCHAR(20) 
)



# 添加表级约束
/*
语法:在各个字段的最下面
[constraint 约束名] 约束类型(字段名)


通用的写法
CREATE TABLE IF EXISTS stuinfo(
	id INT PRIMARY KEY,
	stuname VARCHAR(20) NOT NULL,
	sex CHAR(1),
	age INT DEFAULT 18,
	seat INT UNIQUE,
	marjorid INT,
	
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)
)

*/


DROP TABLE IF EXISTS stuinfo
SELECT * FROM stuinfo
CREATE TABLE stuinfo(
	id INT,
	stuName VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT,
	
	CONSTRAINT pk PRIMARY KEY(id),
	CONSTRAINT uq UNIQUE(seat),
	CONSTRAINT ck CHECK(gender='男' OR gender='女'),
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)# 外键
)

SHOW INDEX FROM stuinfo

CREATE TABLE stuinfo(
	id INT,
	stuName VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT,
	
	PRIMARY KEY(id),
	UNIQUE(seat),
	CHECK(gender='男' OR gender='女'),
	FOREIGN KEY(majorid) REFERENCES major(id)# 外键
)



# 二、 修改表时添加约束
/*
1. 田间列级约束
alter table 表名 modify column 字段名 字段类型 新约束

2. 添加表级约束
alter table 表名 add [constraint 约束名] 约束类型(字段名) 【外键的引用】 
*/

DROP TABLE IF EXISTS stuinfo
SELECT * FROM stuinfo
CREATE TABLE stuinfo(
	id INT,
	stuName VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT
	

)

DESC stuinfo
# 1. 添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NOT NULL;

ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;

# 2.表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id);


# 三、 修改表时删除约束

# 删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NULL;
# 删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT;
# 删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;
# 删除唯一
ALTER TABLE stuinfo DROP INDEX seat;

# 删除外键约束
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;








# 标识列
/*
又称自增长列
可以不用手动插入值系统提供默认的序列值

1. auto_increment 的标识列必须是一个key
2. 一个表之多i一个标识列
3. 标识列的类型只能是数值型
4. 通过SET auto_increment_increment = 3;设置步长

CREATE TABLE tab_indentity(
	id INT PRIMARY KEY AUTO_INCREMENT,
	...
)


*/


SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment = 3; -- 设置步长为3

# 修改表时设置标识列
ALTER TABLE tab_indentity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT
# 修改表时删除标识列
ALTER TABLE tab_indentity MODIFY COLUMN id INT PRIMARY KEY;

四、TCL(事务控制)语言的学习

4.1 事务和事务的处理

# TCL
/*
Transaction Control Language 事务控制语言
事务:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行

事务的ACID属性
1. 原子性(Atomicity)
原子性是指事务是不可分割的单位,事务中的操作要么都发生,要么都不发生
2. 一致性(Consistency)
事务必须必须使数据库从一个一致性状态变换到另一个一致性状态
3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能倍其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,
并发执行的各个事务之间不能互相干扰
4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中的数据的改变就是永久性的,接下来的其他操作
和数据库故障不应该对其有任何影响。

/*数据库的隔离级别

对于同时运行的多个事务,当这些事务访问数据库相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题

   	脏读:
		对于两个事务T1,T2,T1读取了已经被T2更新但是还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的。
	不可重复读:
		对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了。
	幻读:
		对于两个事务T1,T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,如果T1再次读取同一个表,就会多出几行



数据库事务的隔离性:
	数据库系统必须具有隔离并发运行各个事务的能力,是他们不会相互影响,避免各种并发问题
	
一个事务于其他事务隔离的程度称为隔离级别,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱


数据库提供的四种事务隔离级别:
READ UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更,脏读,不可重复度和幻读的问题都会出现
READ COMMITED(读已提交数据):只允许事务读取已经被其他事务提交的变更,可以避免脏读,但是不可重复读和幻读问题仍然可能出现
REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读问题仍然存在
SERIALIZABLE(串行化):确保事务可以从一个表中读取相同的行,在这个事务持续期间禁止其他事务对该表执行插入,更新和删除操作,所有并发问题都可以避免,但性能十分低下



Oracle支持的两种事务隔离级别:READ COMMITED、SERIALIZABLE,默认为READ COMMITED
mysql支持四种事务隔离级别:默认为REPEATABLE READ

*/

# 查看当前隔离级别
SELECT @@tx_isolation;
# 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;





# 事务的创建
隐式事务:事务没有明显的开启和结束的标记
比如insertupdatedelete


显示事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用
set autocommit = 0 

步骤1:开启事务
set autocommit = 0;
[start transaction;]
步骤2:编写事务中的sql语句(select insert update delete)
语句1;
语句2;
...

步骤3:结束事务
commit;提交事务
或rollback;回滚事务







*/


# 案例:转账
/*
张三丰 1000
郭襄   1000

update 表 set 张三丰的余额=500 where name = '张三丰'
update 表 set 郭襄的余额=500 where name = '郭襄'

*/


DROP DATABASE test;
CREATE DATABASE test CHARACTER SET utf8;
DROP TABLE IF EXISTS account;

CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(20),
	balance DOUBLE
)


INSERT INTO account(username,balance)
VALUES('张三丰',1000),('郭襄',1000)

# 演示事务的使用步骤

# 开启事务
SET autocommit = 0;
START TRANSACTION;

# 编写一组事务的语句
UPDATE account SET balance = 500 WHERE username = '张三丰';
UPDATE account SET balance = 1500 WHERE username ='郭襄';
# 结束事务
COMMIT;

SELECT * FROM account;


# 开启事务
SET autocommit = 0;
START TRANSACTION;

# 编写一组事务的语句
UPDATE account SET balance = 1000 WHERE username = '张三丰';
UPDATE account SET balance = 1000 WHERE username ='郭襄';
# 结束事务
ROLLBACK;



/* delete 和truncate 在事务使用时的区别*/

-- 演示delete
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
# 可以回滚,数据还在


-- 演示truncate
SET autocommit = 0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;
# 不能回滚,数据没有了

五、视图的讲解

# 视图
/*
含义:虚拟表 和普通表一样使用,mysql5.1版本出现的新特性,是通过表动态生成的数据,只保存了sql逻辑,不保存查询结果i


视图的创建
语法: 
create view 视图名
as
查询语句


视图的优势:
重用sql语句
简化复杂的sql操作,不必知道它的查询细节
保护数据安全

*/

# 案例:查询姓张的学生名和专业名
SELECT * stuname,majorname
FROM stuinfo s 
INNER JOIN major m ON s.majorid=m.id
WHERE s.stuname LIKE '张%';

# 使用视图

CREATE VIEW v1
AS
SELECT * stuname,majorname
FROM stuinfo s 
INNER JOIN major m ON s.majorid=m.id


SELECT * FROM v1 WHERE stuname LIKE '张%';


# 1. 查询姓名中中包含a字符的员工名、部门名和工种信息

#创建视图
CREATE VIEW myv1
AS
SELECT last_name,department_name,job_title,email
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN jobs j ON j.job_id = e.job_id;


# 使用视图
SELECT * FROM myv1 WHERE last_name LIKE '%a%';


# 查询各部门的平均工资级别
# 创建视图
CREATE VIEW myv2
AS SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

# 使用
SELECT * FROM myv2;

SELECT myv2.ag,g.`grade_level`
FROM myv2
JOIN job_grades g
ON myv2.`ag` BETWEEN g.`lowest_sal` AND g.`highest_sal`


# 查询平均工资最低的部门名和工资
CREATE VIEW myv3
AS 
SELECT * FROM myv2 ORDER BY ag LIMIT 1;

SELECT * FROM myv3;



#二、视图的修改
/*
方式一:
create or replace view 视图名
as 
查询语句
如果视图存在就修改
不存在则创建


# 方式二
alter view 视图名
as 
查询语句

*/

# 修改视图3
CREATE OR REPLACE VIEW myv3
AS 
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

SELECT * FROM myv3




# 删除视图
/*
语法:
drop view 视图名,视图名.....

*/

 
DROP VIEW myv1,myv2,myv3


# 查看视图
DESC myv1;

SHOW CREATE VIEW myv1



# 视图的更新
/*

*/

CREATE OR REPLACE VIEW myv1
AS 
SELECT last_name,email,salary
FROM employees;



# 插入
INSERT INTO myv1 VALUES('张飞','1283@qq.com',4000)
SELECT * FROM myv1
# 修改
UPDATE myv1 SET last_name='张无忌' WHERE last_name='张飞';

# 删除
DELETE FROM myv1 WHERE last_name = '张无忌'

 

/*
具备以下特点的视图不允许更新
包含以下关键字的sql语句:分组函数,distinct,group by,having,union或者union all
常量视图
select 中包含子查询
join
from 一个不能更新的视图
where子句的子查询引用from子句中的表

*/






/*视图与表的对比


		创建语法的关键字	是否实际占用物理空间	使用 

视图:		create view		只是保存了sql逻辑	增删改查,只是一般不能增删改
表:		create table		保存了数据		增删改查

*/





六、变量

/*变量的介绍


分类:
	系统变量:
		全局变量
		会话变量
	
	自定义变量:
		用户变量
		局部变量

全局变量:
	作用域:服务器每次启动将为所有的全局变量赋初始值,针对与所有的会话有效,但是不能跨重启

*/


# 一、系统变量
-- 说明:变量由系统提供,不是用户定义的,数据服务器层面
/*
使用语法:
	1、 查看所有的系统变量
	show global/session(全局/会话【不写代表会话】)) variables
*/

SHOW GLOBAL VARIABLES;
SHOW VARIABLES;
# 查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';


# 查看指定某个系统变量的值
-- 语法:select @@global/[session].系统变量名

# 为某个系统变量赋值
-- 语法一:	set @@global/[session] 系统变量名 = 值;
-- 语法二: 	set global/[session].系统变量名 = 值;


# 演示 
SHOW GLOBAL VARIABLES;


# 查看指定的全局变量的值
SELECT @@global.autocommit;
SELECT @@tx_isolation;

# 为指定的全局变量赋值
SET @@global.autocommit=0;










/*
会话变量

作用域:仅仅针对当前会话有效
*/

# 查看所有的会话变量
SHOW SESSION VARIABLES










/*自定义变量

说明:用户自定义的
使用步骤:
	声明
	赋值
	使用(查看、比较、运算等)
	
1.用户变量
作用域:针对与当前会话有效,同于会话变量的作用域



2.局部变量
作用域:仅仅在定义它的begin end中有效

应用在begin end中的第一句话


*/
#用户变量
#①声明并初始化
/*
SET @用户变量名=值;
SET @用户名:=值;
SELECT @用户变量名:=值;
*/
# ②赋值(更新用户变量的值)
/*方式一:
	通过set或select
	set @用户变量名 = 值;
	set @用户变量名: = 值;
	select @用户变量名: = 值;
方式二:
	select 字段 into 变量名 from 表
*/

# ③查看
/*
select @用户变量名
*/


SET @name='zhoulinfeng';
SELECT @name;
SELECT @name:='zhouxuan';

SET @cout = 3;
SELECT @cout;

SELECT COUNT(*) INTO @cout FROM employees


# 局部变量
# ①声明
/*
DECLARE 变量名 类型;
DECLARE 变量名 类型 DEFAULT 值;
*/

# ②赋值

/*
方式一:
	通过set或select
	SET 用户变量名 = 值;
	SET 用户变量名: = 值;
	SELECT @用户变量名: = 值;
方式二:
	SELECT 字段 INTO 变量名 FROM 表
*/


#使用
-- select 局部变量名


七、存储过程和函数

7.1 存储过程

/*

存储过程和函数:类似于java中的方法
好处:
	提高代码的重用性
	简化操作

存储过程:
	含义:一组预先编译好的sql语句集合,理解成批处理语句
	好处:
		1. 提高了代码的重用性
		2. 简化了操作
		3. 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
	


#存储过程创建语法
create procedure 存储过程名(参数列表)begin
	存储过程体
end

注意:
	1. 参数列表包含三部分:参数模式 参数名 参数类型
	举例:in stuname varchar(20) 
	参数模式:
		in  	:该参数可以作为输入,也就是说该参数需要调用方传入值
		out	:该参数可以作为返回值,
		inout	:该参数既可以作为输入,又可以作为输出。
	2. 如果存储过程体仅仅只有一句话,begin end 可以省略
	3. 存储过程中的每条sql语句的结尾要求必须加分号
	4. 存储过程的结尾可以使用delimiter重新设置
		语法: delimiter 结束标记
		举例: delimiter $
		
		
		
调用语法:
	call 存储过程名(实参列表)

*/

# 1. 空参列表
# 插入到admin表中5条数据

DELIMITER $
CREATE PROCEDURE myp2()
BEGIN
	INSERT INTO admin(username,`password`) VALUES('john1','00001'),('zhouxuan','1231231');
END $

# 调用
CALL myp1()$

# 创建带有in模式参数的存储过程
# 案例1:创建存储过程实现,根据女生名,查询对应的男生信息
DROP girls;

DELIMITER $
CREATE PROCEDURE myp3(IN beautyName VARCHAR(20))
BEGIN
	SELECT bo.*
	FROM boys bo
	RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
	WHERE b.name = beautyName;
END $

DELIMITER $
CALL myp3('热巴') $

# 案例2:创建存储过程实现,用户是否登录成功
DELIMITER $
CREATE PROCEDURE myp4(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
	DECLARE result VARCHAR(20) DEFAULT '';
	
	SELECT COUNT(*) INTO result
	FROM admin 
	WHERE admin.`username` = username
	AND admin.`password` = PASSWORD;
	
	SELECT result;
END $


CALL myp4('zhoulin','322131');



# 创建带有out模式的存储过程
# 案例1:根据女神名,返回对应的男神名
CREATE PROCEDURE myp4(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
	SELECT bo.boyName INTO boyName
	FROM boys bo 
	INNER JOIN beauty b ON bo.id = b.boyfriend_id
	WHERE b.name = beautyName;
END $ 

SET @bName;
CALL myp5('小昭',@bName)$
SELECT @bName $

# 根据女生名,返回对应的男生名和男生魅力值
CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT userCP INT)
BEGIN
	SELECT bo.boyName,bo.userCP INTO boyName,userCP
	FROM boys bo
	INNER JOIN beauty b ON bo.id = b.boyfriend_id
	WHERE b.name=beautyName;
END $


CALL myp6('小昭',@name,@cp)
SELECT @name,@cp $


# 创建带inout模式参数的存储过程

# 案例1:传入a和b两个值,最终a和b都翻倍并返回
DELIMITER $
CREATE PROCEDURE myp8(INOUT a INT, INOUT b INT)
BEGIN 
	SET a = a*2;
	SET b = b*2;
END $

SET @m =10 $
SET @n =20 $
CALL myp8(@m,@n) $
SELECT @m;
SELECT @n












/*存储过程的删除
语法:
	drop procedure 存储过程名 (只能一次删除一个)
	
*/

DROP PROCEDURE myp1;



# 查看存储过程的信息
/*
语法:show create procedure myp2
*/
SHOW CREATE PROCEDURE myp2;

7.2 函数

/*
函数
含义:一组预先编译好的sql语句集合,理解成批处理语句
	好处:
		1. 提高了代码的重用性
		2. 简化了操作
		3. 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率


同存储过程的区别

存储过程:可以有0个返回,也可以有多个
函数:有且仅有一个返回



# 创建语法
create function 函数名(参数列表) returns 返回类型
begin
	函数体
end


注意:
	参数列表包含两部分:
		参数名 参数类型
	函数体:肯定会有return语句,如果没有会报错
	如果return语句没有放在函数体的最后也不会报错,但不建议
	函数体中仅有一句话,则可以省略begin end;
	使用delimiter 语句设置结束标记







# 调用语法
select 函数名(参数列表)

*/
# 无参有返回
# 案例:返回公司的员工个数
DELIMITER $
CREATE FUNCTION myf1() RETURNS INT
BEGIN
	DECLARE c INT DEFAULT 0; 
	SELECT COUNT(*) INTO c  
	FROM `employees`;
	RETURN c;
END $


SELECT myf1();



# 2. 有参数返回
# 案例1:根据员工名返回它的工资
DELIMITER $
CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN 
	SET @sal=0;
	SELECT salary INTO @sal
	FROM employees
	WHERE last_name=empName;
	
	RETURN @sal;
END $

SELECT myf2('Kochhar') $





# 案例2:根据部门名:返回该部门的平均工资

DELIMITER $
CREATE FUNCTION myf3(in_department_id INT) RETURNS DOUBLE
BEGIN 
	DECLARE sal DOUBLE;
	SELECT AVG(salary) INTO sal
	FROM employees
	WHERE department_id = in_department_id
	GROUP BY department_id;
	RETURN sal;
	
END $



# 查看函数
-- show create function 函数名;
SHOW CREATE FUNCTION myf2;

# 删除函数
-- drop function 函数名;
DROP FUNCTION myf1



八、流程控制结构

/*
流程控制结构



分类:
	顺序结构:程序从上往下一次执行
	分支结构:程序从两条或多条路径中选择一条执行	
	循环结构: 程序在满足一定条件的基础上,重复执行一段代码



# 一、分支结构
	1. if函数
	
	功能:实现简单的双分支
	语法:
	select if(表达式1,表达式2,表达式3)
	执行顺序:如果表达式1成立,返回表达式2的值,否则返回表达式3的值

	应用:任何地方


	2. case结构
	情况1:类似于java中的switch语句
	语法:
		case 变量|表达式|字段
		when 要判断的值 then 返回的值1或语句1;
		when 要判断的值 then 返回的值2或语句2;
		when 要判断的值 then 返回的值3或语句3;
		...
		else 要返回的值n;
		end case;
	情况2:类似于java中的多重if语句,一般用于实现区间判断
	语法:
		case
		when 要判断的条件1 then 返回的值1或语句1;
		when 要判断的条件2 then 返回的值2或语句2;
		when 要判断的条件3 then 返回的值3或语句3;
		...
		else 要返回的值n;
		end case;
	特点:
		可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或begin end外面
		可以作为独立的语句去使用,只能放在begin end中
		else可以省略,如果when中条件都不满足,返回null
		
		
	3. if结构
	功能:实现多重分支
	语法:
		if 条件1 then 语句1;
		elseif 条件2 then 语句2;
		...
		[else 语句n;]
		end if;
		
	特点:只能应用在begin end 中





# 二、 循环结构
必须放在begin end里面


分类:
	while、loop、repeat

循环控制:
	iterate 类似于continue,继续,结束本次循环,继续下一次
	leave类似于break,跳出,结束当前所在的循环
	
	
## 1、while
	语法:
		[标签:]while 循环条件 do
			循环体
		end while [标签];

## 2、loop

	语法:
		[标签:]loop
			循环体
		end loop [标签];
		
	可以用来模拟简单的死循环


## 3、repeat
	语法:
		[标签:] repeat
			循环体
		until 结束循环的条件
		end repeat [标签];
*/
# 案例:创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示A,80-90,显示b,60-80显示c,否则,显示d

DELIMITER $
CREATE PROCEDURE test_case(IN score INT)
BEGIN
	CASE 
	WHEN score >= 90 THEN SELECT 'A';
	WHEN score >= 80 THEN SELECT 'B';
	WHEN score >= 60 THEN SELECT 'C';
	ELSE SELECT 'D';
	END CASE;
END $

CALL test_case(95) $



# 案例使用if结构,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示A,80-90,显示b,60-80显示c,否则,显示d
DELIMITER $
CREATE FUNCTION test_if(score INT) RETURNS CHAR
BEGIN
	IF score >= 90 THEN RETURN 'A';
	ELSEIF score >= 80 THEN RETURN 'B';
	ELSEIF score >=60 THEN RETURN 'C';
	ELSE RETURN 'D';
	END IF;
END $

SELECT test_if(33)





# 循环结构
# 案例:批量插入,根据次数插入到admin表中多条记录
DELIMITER $
CREATE PROCEDURE pro_while(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	WHILE i < insertCount DO
		INSERT INTO admin (username,`password`) VALUES(CONCAT('rose',i),'666');
		
		SET i  = i + 1;
		
	END WHILE ;
END $
CALL pro_while(100)

SELECT * FROM admin;


# 添加leave语句
# 案例:批量插入,根据次数插入到admin表中多条记录,次数大于20 则停止

DELIMITER $
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	a:WHILE i < insertCount DO
		INSERT INTO admin (username,`password`) VALUES(CONCAT('xiaohua',i),'666');
		IF i >= 20 THEN LEAVE a;
		END IF;
		SET i  = i + 1;
	END WHILE a;
END $
CALL pro_while1(100)
SELECT * FROM admin;


# 添加iterate
# 案例:批量插入,根据次数插入到admin表中多条记录,至添加偶数次
DELIMITER $
CREATE PROCEDURE pro_while2(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	a:WHILE i < insertCount DO
		INSERT INTO admin (username,`password`) VALUES(CONCAT('xiaohua',i),'666');
		IF MOD(i,2) != 0 THEN ITERATE a;
		END IF;
		SET i  = i + 1;
	END WHILE a;
END $
CALL pro_while2(100)
SELECT * FROM admin;




















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值