数据库笔记整理

1. 数据库的相关概念

1.1 DBMS

1.2 DB

1.3 SQL

2. 数据库存储数据的特点

3. 初始MySQL

3.1 MySQL产品的介绍

3.2 MySQL服务的启动和停止

方式一:计算机——右击管理——服务
方式二:通过管理员身份运行
net start 服务名(启动服务)
net stop 服务名(停止服务)

3.3 MySQL服务的登录和退出

方式一:通过mysql自带的客户端
只限于root用户

方式二:通过windows自带的客户端
登录:
mysql 【-h主机名 -P端口号 】-u用户名 -p密码

退出:
exit或ctrl+C

3.4 MySQL的常见命令和语法规范

1.查看当前所有的数据库
show databases;
2.打开指定的库
use 库名
3.查看当前库的所有表
show tables;
4.查看其它库的所有表
show tables from 库名;
5.创建表
create table 表名(

	列名 列类型,
	列名 列类型,
	。。。
);
6.查看表结构
desc 表名;


7.查看服务器的版本
方式一:登录到mysql服务端
select version();
方式二:没有登录到mysql服务端
mysql --version
或
mysql --V

3.5 SQL的语言分类

DQL(Data Query Language):数据查询语言
	select 
DML(Data Manipulate Language):数据操作语言
	insert 、update、delete
DDL(Data Define Languge):数据定义语言
	create、drop、alter
TCL(Transaction Control Language):事务控制语言
	commit、rollback

4. DQL语言的学习

4.1 基础查询

#进阶1:基础查询
/*
语法:
select 查询列表 from 表名;
特点:
1、查询列表可以是:表中的字段、常量值、表达式、函数
2、查询的结果是一个虚拟的表格
*/
USE myemployees;

# 1.查询表中的单个字段
SELECT last_name FROM employees;

# 2.查询表中的多个字段
SELECT 
  `first_name`,
  `last_name`,
  `email` 
FROM
  employees;

# 3.查询表中的所有字段
SELECT * FROM employees;

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

# 5.查询表达式
SELECT 100%98;

# 6.查询函数
SELECT VERSION();

# 7.起别名
/*
1.便于理解
2.如果要查询的字段有重名的情况,使用别名可以区分来

*/
# 方式一:
SELECT 100%98 AS 结果;
SELECT last_name AS 姓, first_name AS 名 FROM employees;
# 方式二:
SELECT last_name 姓, first_name 名 FROM employees;

# 案例:查询salary,显示结果为 out put,加双引号
SELECT salary "out put" FROM employees;

# 8.去重
# 案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT `department_id` FROM employees;

# 9.+号作用
/*
运算符:两个操作数都为数值型,则作加法运算
其中一方为数值型,试图将字符型数值转换为数值型,如果转换成功,则继续做加法运算
如果转换失败,则将字符型数值转换成0
如果一方为null,则结果肯定为null
*/
# 案例:员工名和姓连接成一个字段,并显示为姓名
SELECT last_name + first_name AS 姓名 FROM employees;
SELECT CONCAT('a', 'b', 'c') AS 结果;
SELECT CONCAT(last_name, first_name) AS 姓名 FROM employees;

# 10.显示departments的结构,并查询其中的全部数据
DESC departments;
SELECT * FROM departments;

# 11.显示出表employees中的全部job_id(不能重复)
SELECT DISTINCT job_id FROM employees;

# 12.显示出表employees的全部列,各个列之间用逗号连接,列头显示成OUT_PUT
SELECT IFNULL(commission_pct, 0) AS 奖金率, commission_pct FROM employees;
SELECT CONCAT(`first_name`, ',', `last_name`, ',', `email`, ',', IFNULL(commission_pct, 0)) AS out_put FROM employees;




4.2 条件查询

# 进阶2:条件查询
/*
语法:
	select 
		查询列表
	from
		表名
	where
		筛选条件;

分类:
	1.按条件表达式筛选
	条件运算符: > < = != <> >= <=
	2.按逻辑表达式筛选
	逻辑运算符:用于连接条件表达式
		&& || !
		and or not
	3.模糊查询
		like
		between and
		in
		is null
*/

# 1. 按条件表达式筛选
# 案例1:查询工资>12000的员工信息 
SELECT * FROM employees WHERE salary > 12000;

# 案例2:部门编号不等于90号的员工名和员工编号
SELECT last_name, department_id FROM employees WHERE department_id <> 90;

# 2.按逻辑表达式筛选
# 案例3:查询工资在1000020000之间的员工名、工资和奖金
SELECT last_name, salary, commission_pct FROM employees WHERE salary > 10000 AND salary < 20000;

# 案例2:部门编号不是在90110之间,或者工资高于15000的员工信息
SELECT 
  * 
FROM
  employees 
WHERE department_id < 90 
  OR department_id > 110 
  OR salary > 15000;
  
SELECT * FROM employees WHERE NOT(department_id >= 90 AND department_id <= 110) OR salary > 15000;


# 3.模糊查询
/*
like:和通配符搭配使用
通配符:% 任意多个字符,包含0个字符     _ 任意单个字符
between and
in
is null | is not null
*/

# 1.like
# 案例1:查询员工名中包含字符a的员工信息
SELECT * FROM employees WHERE last_name LIKE '%a%';
# 案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
SELECT last_name, salary FROM employees WHERE last_name LIKE '__n_l%';
# 案例3:查询员工名中第二个字符为_的员工名(转义字符的使用)
SELECT last_name FROM employees WHERE last_name LIKE '_\_%';
SELECT last_name FROM employees WHERE last_name LIKE '_a_%' ESCAPE 'a';

# 2.between and
/*
使用between and包含临界值,不能调换顺序
*/
# 案例1:查询员工编号在100120之间的员工信息
SELECT * FROM employees WHERE employee_id >=100 AND employee_id <=120;
SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;

# 3.in
/*
判断某字段的值是否属于in列表中的某一项
特点:in列表的值类型必须一致或兼容、不支持通配符
*/
# 案例1:查询员工的工种编号是 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';
SELECT last_name, job_id FROM employees WHERE job_id IN('IT_PROG', 'AD_VP', 'AD_PRES');

# 4.is null
/*
=或<>不能用于判断null值
*/
# 案例1:查询没有奖金的员工名和奖金率
SELECT last_name, commission_pct FROM employees WHERE commission_pct IS NULL;
# 案例2:查询有奖金的员工名和奖金率
SELECT last_name, commission_pct FROM employees WHERE commission_pct IS NOT NULL;


# 5.安全等于<=>
SELECT last_name, commission_pct FROM employees WHERE commission_pct <=> NULL;
# 案例2:查询工资为12000的员工信息
SELECT last_name, salary FROM employees WHERE salary <=> 12000;


# 6.查询员工号为176的员工的姓名和部门号和年薪
SELECT last_name, department_id, salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪 FROM employees WHERE employee_id = 176;

# 7.查询没有奖金,且工资小于18000的salary,last_name
SELECT last_name, salary FROM employees WHERE commission_pct IS NULL AND salary < 18000;

# 8.查询employees表中,job_id不为'IT'或者工资为12000的员工信息
SELECT * FROM employees WHERE job_id <> 'IT' OR salary = 12000;

# 9.查看部门departments表的结构
DESC departments;

# 10.查询部门departments表中涉及哪些位置编号
SELECT DISTINCT location_id FROM departments;

4.3 排序查询

# 进阶3:排序查询
/*
语法:
	select 查询列表
	from 表
	[where 筛选条件]
	order by 排序列表 [asc | desc]
	
特点:
	order by 子句中支持单个字段、多个字段、表达式、函数、别名
	order by 一般放在查询语句的最后面,limit子句除外
	

*/

# 案例1:查询员工信息,要求工资从高到低排序
SELECT * FROM employees ORDER BY salary DESC;
# 案例2:查询员工信息,要求工资从低到高排序(默认)
SELECT * FROM employees ORDER BY salary ASC;


# 案例3:查询部门编号>=90的员工信息,按入职事件的先后顺序进行排序
SELECT * FROM employees WHERE department_id >= 90 ORDER BY hiredate;

# 案例4:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT 
  *,
  salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪 
FROM
  employees 
ORDER BY salary * 12 * (1 + IFNULL(commission_pct, 0)) DESC;

# 案例5:按年薪的高低显示员工的信息和年薪【按别名排序】
SELECT 
  *,
  salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪 
FROM
  employees 
ORDER BY 年薪 DESC;

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

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

# 案例8:查询员工的姓名和部门号和年薪,按年薪降序 按姓名升序
SELECT last_name, department_id, salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪 FROM employees ORDER BY 年薪 DESC, last_name ASC;

# 案例9:选择工资不在800017000的员工的姓名和工资,按工资降序
SELECT last_name, salary FROM employees WHERE salary NOT BETWEEN 8000 AND 17000 ORDER BY salary DESC;
SELECT last_name, salary FROM employees WHERE NOT(salary BETWEEN 8000 AND 17000) ORDER BY salary DESC;

# 案例10:查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT * FROM employees WHERE email LIKE '%e%' ORDER BY LENGTH(email) DESC, department_id ASC;

4.4 常见函数

4.4.1 单行函数

在这里插入图片描述

/*
一、常见函数
调用:select 函数名(实参列表)【from 表】;
分类:
	单行函数:concat、length、ifnull等
	分组函数:做统计使用

常见函数:
	字符函数:
	length
	concat
	substr
	instr
	trim
	upper
	lower
	lpad
	rpad
	replace
	
	数学函数:
	round 
	ceil
	floor
	truncate
	mod
	
	日期函数:
	now
	curdate
	curtime
	year
	month
	monthname
	day
	hour
	minute
	second
	str_to_date
	date_format
	
	其他函数:
	version
	database
	user
	
	控制函数
	if
	case
	
*/

# 1.字符函数
# 1.1 length获取参数值的字节个数
SELECT LENGTH('john');
SELECT LENGTH('张三丰hahaha');

SHOW VARIABLES LIKE '%char%';

# 1.2 concat 拼接字符串
SELECT CONCAT(last_name, '_', first_name) AS 姓名 FROM employees;

# 1.3 upper lower
SELECT UPPER('john');
SELECT LOWER('joHn');
# 示例:将姓变成大写,名变成小写,然后拼接
SELECT CONCAT(UPPER(last_name), '_', LOWER(first_name)) 姓名 FROM employees; 

# 1.4 substr
# 注意索引从1开始
SELECT SUBSTR('李莫愁爱上了陆展元', 6) out_put;
SELECT SUBSTR('李莫愁爱上了陆展元', 1, 3) out_put;
# 案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name, 1, 1)), '_', LOWER(SUBSTR(last_name, 2))) out_put FROM employees;

# 1.5 instr 返回子串第一次出现的索引,如果找不到返回0
SELECT INSTR('杨不悔爱上了殷六侠', '殷六侠') AS out_put;

# 1.6 trim 只能去除前后的字符
SELECT LENGTH(TRIM('  张翠山   ')) AS out_put;
SELECT TRIM('aaa' FROM 'aaaaaaaa张aaa翠山aaaaaaaaaaaa') AS out_put;

# 1.7 lapd 用指定的字符实现左填充指定的长度
SELECT LPAD('殷素素', 10, '*') AS out_put;

# 1.8 rapd 用指定的字符实现右填充指定的长度
SELECT RPAD('殷素素', 12, 'ab') AS out_put;

# 1.9 replace 替换
SELECT REPLACE('周芷若周芷若张无忌爱上周芷若', '周芷若', '赵敏') AS out_put;

# 2.数学函数
# 2.1 round 四舍五入
SELECT ROUND(1.65);
SELECT ROUND(1.567, 2);

# 2.2 ceil 向上取整,返回>=该参数的最小整数
SELECT CEIL(1.20);

# 2.3 floor 向下取整,返回<=该参数的最大整数
SELECT FLOOR(-9.99);

# 2.4 truncate 截断
SELECT TRUNCATE(1.69999, 2);

# 2.5 mod 取余
SELECT MOD(10, 3);


# 3.日期函数
# 3.1 now 返回当前系统日期+时间
SELECT NOW();

# 3.2 curdata 返回当前系统日期,不包含时间
SELECT CURDATE();

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

# 可以获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW());
SELECT YEAR('1998-1-1');

SELECT YEAR(hiredate) 年 FROM employees;
SELECT MONTH(NOW());
# 英文
SELECT MONTHNAME(NOW());

# 3.4 str_to_date:将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE('1998-3-2', '%Y-%c-%d') AS out_put;

# 查询入职日期为1992-4-3的员工信息
SELECT * FROM employees WHERE hiredate = '1992-4-3';
SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992', '%c-%d %Y');


# 3.5 date_format:将日期转换成字符
SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日') AS out_put;

# 查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name, DATE_FORMAT(hiredate, '%m月/%d日 %Y年') AS 入职日期 FROM employees WHERE commission_pct IS NOT NULL;


# 4.其他函数
SELECT VERSION();
SELECT DATABASE();
SELECT USER();

# 5.流程控制函数
# 5.1 if函数:实现if else效果
SELECT IF(10 > 5, '大', '小');
SELECT last_name, commission_pct, IF(commission_pct IS NULL, '没奖金,呵呵', '有奖金,嘻嘻') AS 备注 FROM employees;

# 5.2 case函数的使用一:switch case的效果
/*
语法:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量1 then 要显示的值1或语句1;
...
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;
 
 # 5.3 case函数的使用二:类似多重if
 /*
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 AS 工资级别
FROM employees;
 
 
# 1.显示系统时间(注:日期+时间)
SELECT NOW();

# 2.查询员工号,姓名,工资,以及工资提高百分之20后的结果(new salary)
SELECT employee_id, last_name, salary, salary * 1.2 AS 'new salary' FROM employees;

# 3.将员工的姓名按首字母排序,并且写出姓名的长度
SELECT LENGTH(last_name) AS '长度', SUBSTR(last_name, 1, 1) AS '首字母' FROM employees ORDER BY 首字母;

# 4.做一个查询,产生下面的结果
/*
<last_name> earns <salary> monthly but wants <salary * 3> Dream Salary
*/
SELECT CONCAT(last_name, ' earns ', salary, " monthly but wants ", salary * 3) AS  'Dream Salary' FROM employees WHERE salary = 24000;

# 5. 使用case,按照下面条件
/*
job      grade
AD_PRES    A
ST_MAN     B
IT_PROG    C
*/
SELECT last_name, job_id AS job,
CASE job_id
WHEN 'AD_PRES' THEN 'A'
WHEN 'ST_MAN ' THEN 'B'
WHEN 'IT_PROG' THEN 'C'
WHEN 'SA_REP'  THEN 'D'
WHEN 'ST_CLERK'  THEN 'E'
END AS Grade
FROM employees
WHERE job_id = 'AD_PRES';

4.5 分组函数

# 二、分组函数
/*
功能:用做统计使用,又称为聚合函数或统计函数或组函数
分类:
	sum、 avg、 max、 min、 count
特点:
1.所有分组函数都忽略null
2.可以和distinct搭配实现去重的运算
3.count函数的详细介绍:
	一般使用count(*)统计行数
4.和分组函数一同查询的字段有限制:要求是group by后的字段
*/

# 1.简单使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT MIN(salary) FROM employees;
# 计算非null的个数
SELECT COUNT(salary) FROM employees;
SELECT SUM(salary), AVG(salary) 平均, MAX(salary) 最高, MIN(salary) 最低, COUNT(salary) 个数 FROM employees;
SELECT SUM(salary), ROUND(AVG(salary), 2) 平均, MAX(salary) 最高, MIN(salary) 最低, COUNT(salary) 个数 FROM employees;

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

# 3.count函数的详细介绍
SELECT COUNT(salary) FROM employees;
# 用于统计总行数
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
# 可以相当于在表中加了一列的常量值
SELECT COUNT('翠霞') FROM employees;
# 效率:
/*
MYISAM存储引擎下,count(*)的效率最高
INNODB存储引擎下,count(*)和count(1)的效率差不多
*/

# 4.和分组函数一同查询的字段有限制
# 不建议显示
SELECT AVG(salary), employee_id FROM employees;

# 5.查询员工工资的最大值、最小值、平均值、总和
SELECT SUM(salary), ROUND(AVG(salary), 2) 平均, MAX(salary) 最高, MIN(salary) 最低 FROM employees;

# 6.查询员工表中最大入职时间和最小入职时间相差天数(DIFFRENCE)
SELECT DATEDIFF('2017-10-1', '2017-9-29');
SELECT DATEDIFF(MAX(hiredate), MIN(hiredate)) AS DIFFRENCE FROM employees;

# 7.查询部门编号为90的员工个数
SELECT COUNT(*) FROM employees WHERE department_id = 90;

4.6 分组查询

#进阶5:分组查询
/*
语法:
	select 分组函数,列(要求出现在group by的后面)
	from 表
	【where 筛选条件】
	group by 分组的列表
	【order by 子句】
	
注意:
	查询列表必须特殊,要求是分组函数和group by后出现的字段
	
特点:
	1.分组查询中的筛选条件可以分为两类
				数据源			位置			关键字
		分组前筛选	原始表			group by子句的前面	where
		分组后筛选	分组后的结果集		group by子句的后面	having
		
		1.分组函数做条件肯定是放在having子句中
		2.能用分组前筛选的,优先使用分组前筛选
		
	2.group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开,没有顺序要求),表达式或函数(用的较少)
	3.也可以添加排序
		
*/

# 简单的分组查询
# 案例1:查询每个工种的最高工资
SELECT MAX(salary), job_id
FROM employees
GROUP BY job_id;

# 案列2:查询每个位置上的部门个数
SELECT COUNT(*), location_id
FROM departments
GROUP BY location_id;


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

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


# 添加分组后筛选条件查询
# 案例1:查询哪个部门的员工个数>2(必须要根据前面计算出来的另外一张表,来进行筛选)
SELECT COUNT(*), department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 2;

# 案例2:查询每个工种有奖金的最高员工的工资>12000
SELECT MAX(salary), job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000;

# 案例3:查询领导编号>102的每个领导手下最低工资>5000的领导编号是哪个,以及其最低工资
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING MIN(salary) > 5000;


# 按表达式或函数进行分组
# 案例1:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
SELECT COUNT(*) 员工个数, LENGTH(last_name) 姓名长度
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*) > 5;


# 按多个字段进行分组
# 案例1:查询每个部门的每个工种的平均工资
SELECT AVG(salary), department_id, job_id
FROM employees
GROUP BY department_id, job_id;



# 添加排序
# 案例1:查询每个部门的每个工种的平均工资,并且按平均工资的高低显示
SELECT AVG(salary), department_id, job_id
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id, job_id
HAVING AVG(salary) > 10000
ORDER BY AVG(salary) DESC;

# 1.查询各个job_id的员工工资的最大值、最小值、平均值、总和,并按照job_id升序
SELECT MAX(salary) max_sal, MIN(salary) min_sal, AVG(salary) ag_sal, SUM(salary) sm_sal, job_id
FROM employees
GROUP BY job_id
ORDER BY job_id;

# 2.查询员工最高工资和最低工资的差距(DIFFRENCE)
SELECT MAX(salary) - MIN(salary) AS DIFFRENCE
FROM employees;

# 3.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary) >= 6000;

# 4.查询所有部门的编号、员工数量和工资平均值,并按平均工资降序
SELECT department_id, COUNT(*), AVG(salary), department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC;

# 5.选择具有各个job_id的员工人数
SELECT COUNT(*) 个数, job_id
FROM employees
GROUP BY job_id;

# 5.选择具有各个job_id的员工人数

4.7 连接查询

在这里插入图片描述
在这里插入图片描述

4.7.1 sql92语法

#进阶6:连接查询
/*
含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
笛卡尔乘积现象:表1 有m行, 表2有n行, 结果=m*n行

发生原因:没有有效的连接条件
如何避免:添加有效的连接条件

分类:
	按年代分类:
	sql92标准:仅仅支持内连接
	sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
	
	按功能分类
		内连接:
			等值连接
			非等值连接
			自链接
		外连接:
			左外连接
			右外连接
			全外连接
		交叉连接
		
		
注意:如果为表起了别名,则查询字段就不能使用原来的表名去限定



*/

SELECT * FROM beauty;
SELECT * FROM boys;

# 会导致出现很多重复无效行的现象
SELECT `name`, boyname FROM beauty, boys
WHERE beauty.`boyfriend_id` = boys.`id`;


# 一、sql92标准
# 1.等值连接
/*
1.多表等值连接的结果为多表的交集部分
2.n表连接,至少需要n-1个连接条件
3.多表的顺序没有要求
4.要为表起别名
5.可以搭配前面介绍的所有子句的使用,比如排序、分组、筛选
*/
# 案例1:查询女神名和对应男生名
SELECT 
  `name`,
  boyname 
FROM
  beauty,
  boys 
WHERE beauty.`boyfriend_id` = boys.`id` ;


# 案例2:查询员工名和对应的部门名
SELECT last_name, department_name
FROM employees, departments
WHERE employees.`department_id` = departments.`department_id`;


# 2.查询员工名、工种号、工种名
SELECT last_name, employees.job_id, job_title
FROM employees, jobs
WHERE employees.`job_id` = jobs.`job_id`;

# 为表起别名(连接查询一般都要为表起别名),显示两个表的交集部分
SELECT last_name, e.job_id, job_title
FROM employees e, jobs j
WHERE e.`job_id` = j.`job_id`;


# 3.两个表的顺序是否可以调换(可以)

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

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


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

# 案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT department_name, d.manager_id, MIN(salary)
FROM departments d, employees e
WHERE d.`department_id` = e.`department_id` AND e.`commission_pct` IS NOT NULL
GROUP BY department_name;


# 7.添加排序
# 案例1:查询每个工种的工种名和员工的个数,并且按员工的个数进行排序
SELECT job_title, COUNT(*) 个数
FROM employees e, jobs j
WHERE j.`job_id` = e.`job_id`             # 相当于将两个表合并(不能直接进行合并,对导致冗余),对合并后的表进行操作
GROUP BY job_title
ORDER BY 个数;

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


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

SELECT * FROM job_grades;

# 2.非等值连接
# 案例1:查询出员工的工资和工资级别
SELECT salary, grade_level
FROM employees e, job_grades j
WHERE salary BETWEEN j.`lowest_sal` AND j.`highest_sal` AND j.`grade_level` = 'A';


# 3.自连接
# 案例1:查询员工名以及上级的名称
SELECT e.employee_id, e.last_name, m.employee_id, m.last_name
FROM employees e, employees m 
WHERE e.`manager_id` = m.`employee_id`;



# 案例1:显示员工表的最大工资和工资平均值
SELECT MAX(salary) 最大工资, AVG(salary) 平均工资
FROM employees; 


# 案例2:查询员工表中的employee_id, job_id, last_name,按department_id降序,按salary升序
SELECT employee_id, job_id, last_name
FROM employees
ORDER BY department_id DESC, salary ASC;

# 案例3:查询员工表的job_id中包含a和e的,并且a在e前面
SELECT job_id
FROM employees
WHERE job_id LIKE '%a%e%';

#select s.`name`, g.`name`, r.`score`
#from student s, grade g, result r
#where s.gradeId = g.id and s.id = r.studentNo;

# 案例5:显示当前日期,以及去除前后空格,截取字符串的函数
SELECT NOW();

# 案例6:显示所有员工的姓名、部门号和部门名称
SELECT last_name, e.department_id, department_name
FROM employees e, departments d
WHERE e.`department_id` = d.`department_id`;

# 案例7:查询90号部门员工的job_id和90号部门的location_id
SELECT e.job_id, d.location_id
FROM employees e, departments d
WHERE e.`department_id` = d.`department_id` AND e.`department_id` = 90;

# 案例8:选择所有有奖金的员工的last_name, department_name, location_id, city
SELECT e.last_name, d.department_name, d.location_id, l.city
FROM employees e, departments d, locations l
WHERE e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id`
AND e.`commission_pct` IS NOT NULL;


# 案例9:选在city在Toronto的员工的last_name, job_id, department_id, department_name
SELECT last_name, e.job_id, d.department_id, department_name, l.city
FROM employees e, departments d, locations l
WHERE e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id` AND l.`city` = 'Toronto';

# 案例10:查询每个工种、每个部门的部门名、工种名和最低工资
SELECT job_title, department_name, MIN(salary)
FROM jobs j, departments d, employees e
WHERE j.`job_id` = e.`job_id` AND e.`department_id` = d.`department_id`
GROUP BY j.`job_title`, d.`department_name`;

# 案例11:查询每个国家下的部门个数大于2的国家编号
SELECT country_id, COUNT(*) 部门个数
FROM locations l, departments d
WHERE d.`location_id` = l.`location_id`
GROUP BY country_id
HAVING 部门个数 > 2;

# 案例12:选择指定员工的姓名、员工号,以及他的管理者的姓名和员工号,接轨类似于下面的格式
SELECT e.last_name employees, e.employee_id "Emp#", m.last_name manager, m.employee_id "Mgr#"
FROM employees e, employees m
WHERE e.`manager_id` = m.`employee_id`;

4.7.2 sql99语法

#二、sql99语法
/*
语法:
	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   连接条件
	【where 筛选条件】
	【group by 分组】
	【having 筛选条件】
	【order by 排序条件】
	
分类:
	等值连接
	非等值连接
	自连接

特点:
	inner可以省略

*/

# 1.等值连接
# 案例1:查询员工名、部门名
SELECT last_name, department_name
FROM employees e
INNER JOIN departments d
ON e.`department_id` = d.`department_id`;


# 案例2:查询名字中包含e的员工名和工种名(添加筛选)
SELECT last_name, job_title
FROM employees e
INNER JOIN jobs j
ON e.`job_id` = j.`job_id`
WHERE e.`last_name` LIKE '%e%';


# 案例3:查询部门个数>3的城市名和部门个数(添加分组+筛选)
SELECT l.city, COUNT(*) 部门个数
FROM locations l
INNER JOIN departments d
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` = j.`job_id`
ORDER BY job_title DESC;



# 2、非等值连接
# 案例1:查询员工的工资级别
SELECT salary, grade_level
FROM employees e
INNER JOIN job_grades j
ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal`;


# 案例2:查询每个工资级别>2的个数,并且进行排序
SELECT j.`grade_level`, COUNT(*) 个数
FROM employees e
INNER JOIN job_grades j
ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal`
GROUP BY j.`grade_level`
HAVING COUNT(*) > 20
ORDER BY COUNT(*) DESC;



# 3、自连接
# 案例1:查询员工名字以及上级名字
SELECT e.last_name, m.last_name
FROM employees e
INNER JOIN employees m
ON e.`manager_id` = m.`employee_id`;


# 案例2:查询姓名中包含字符k的员工名字以及上级名字
SELECT e.last_name, m.last_name
FROM employees e
INNER JOIN employees m
ON e.`manager_id` = m.`employee_id`
WHERE e.`last_name` LIKE '%k%';




# 二、外连接
/*
应用场景:查询一个表中有,另一个表中没有的记录
特点:
1.外连接的查询结果为主表中的所有记录
	如果从表中有和他匹配的,则显示匹配的值
	如果从表中没有和它匹配的,则显示null
	外联查询结果=内连接结果+主表中有而从表中没有的记录
2.左外连接:left join左边的是主表
  右外连接:right join右边的是主表
  左外和右外交换两个表的顺序,可以实现同样的效果
  全外连接=内连接的结果+表1中有但是表2没有的+表2中有但是表1没有的
	
*/

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


# 案例1:查询哪个部门没有员工
# 左外
SELECT department_name, e.`employee_id`
FROM departments d
LEFT OUTER JOIN employees e
ON d.`department_id` = e.`employee_id`
WHERE e.`department_id` IS NULL;


# 案例1:查询哪个部门没有员工
# 右外
SELECT department_name, e.`employee_id`
FROM employees e
RIGHT OUTER JOIN departments d
ON d.`department_id` = e.`employee_id`
WHERE e.`department_id` IS NULL;

# 全外


# 交叉连接
SELECT b.*, bo.*
FROM beauty b
CROSS JOIN boys bo;


# 案例1:查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,则用null填充
SELECT b.id, bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE b.`id` > 3;


# 案例2:查询哪个城市没有部门
SELECT l.city, d.*
FROM locations l
LEFT OUTER JOIN departments d
ON l.`location_id` = d.`location_id`
WHERE d.`location_id` IS NULL;

# 案例3:查询部门名为SAL会IT的员工信息
SELECT department_name, e.*
FROM departments d
LEFT OUTER JOIN employees e
ON e.`department_id` = d.`department_id`
WHERE d.`department_name` IN('SAL', 'IT');

4.8 子查询

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

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


#一、where或having后面
# 1.标量子查询(单行子查询)
# 2.列子查询(多行子查询)
# 3.行子查询(多列多行)
/*
特点:
	1.子查询放在小括号内
	2.子查询放在条件的右侧
	3.标量子查询,一般搭配着单行操作符使用、
		> < >= <= = <>
	4.列子查询,一般搭配着多行操作符使用
		IN、any/some、all
	5.子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

*/


# 1.标量子查询
# 案例1:谁的工资比 Abel 高?
SELECT *
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
WHERE job_id = (
	SELECT job_id
	FROM employees
	WHERE employee_id = 141
) AND salary > (
	SELECT salary
	FROM employees
	WHERE employee_id = 143
);

# 案例3:返回公司工资最少的员工的last_name, job_id和salary
SELECT last_name, job_id, salary
FROM employees
WHERE salary = (
	SELECT MIN(salary)
	FROM employees
);

# 案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id, MIN(salary) 最低工资
FROM employees
GROUP BY department_id
HAVING 最低工资 > (
	SELECT MIN(salary)
	FROM employees
	WHERE department_id = 50
);


# 2.列子查询(多行子查询)
# 案例1:返回location_id是14001700的部门中的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id IN (
	SELECT DISTINCT department_id
	FROM departments
	WHERE location_id IN (1400, 1700)
);


SELECT last_name
FROM employees e
INNER JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`location_id` IN (1400, 1700);

# 案例2:返回其他部门中比job_id为‘IT_PROG’部门任一工资低的员工的:工号、姓名、job_id以及salary
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';

# 或者
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
	SELECT MAX(salary)
	FROM employees
	WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG';


# 案例3:返回其他部门中比job_id为'IT_PROG'部门所有工资都低的员工的员工号、姓名、job_id以及salary
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < ALL(
	SELECT salary
	FROM employees
	WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG';


# 3.行子查询(结果集一行多列或多行多列)
# 案例1:查询员工编号最小且工资最高的员工信息
SELECT *
FROM employees
WHERE (employee_id, salary) = (
	SELECT MIN(employee_id), MAX(salary)
	FROM employees
);

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



#二、select后面(只能是一行一列)
# 案例1:查询每个部门的员工个数
# 不能用内连接
SELECT d.*, COUNT(*)
FROM departments d
LEFT OUTER JOIN employees e
ON e.`department_id` = d.`department_id`
GROUP BY d.`department_id`;

#或者(里层是一个连接查询)
SELECT d.*, (
	SELECT COUNT(*) 
	FROM employees e
	WHERE e.`department_id` = d.`department_id`
	) 个数
FROM departments d;


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


#三、from后面
/*
将子查询结果充当一张表,要求必须起别名
*/
# 1.案例1:查询每个部门的平均工资的工资等级
SELECT ag_dep.*, g.`grade_level`
FROM (
	SELECT AVG(salary) ag, department_id
	FROM employees
	GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;



#四、exists后面(相关子查询)
/*
语法:
exists(完整的查询语句)
结果:1或者0
*/
SELECT EXISTS(SELECT employee_id FROM employees WHERE salary <=> 30000);
# 案例1:查询有员工的部门名
SELECT last_name, department_name
FROM employees e
INNER JOIN departments d
ON e.`department_id` = d.`department_id`;

#或者
SELECT department_name
FROM departments d
WHERE d.`department_id` IN (
	SELECT e.department_id
	FROM employees e
);

#或者
SELECT department_name
FROM departments d
WHERE EXISTS(
	SELECT *
	FROM employees e
	WHERE e.`department_id` = d.`department_id`
);

# 案例2:查询没有女朋友的男生信息
SELECT *
FROM boys bo
WHERE bo.`id` NOT IN (
	SELECT boyfriend_id
	FROM beauty
);

SELECT *
FROM boys bo
WHERE NOT EXISTS (
	SELECT *
	FROM beauty
	WHERE beauty.`boyfriend_id` = bo.`id`
);




# 案例1:查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name, salary
FROM employees e
WHERE e.`department_id` = (
	SELECT department_id
	FROM employees
	WHERE employees.`last_name` = 'Zlotkey'
);


# 案例2:查询工资比公司平均工资高的员工的员工号,姓名和工资
SELECT employee_id, last_name, salary
FROM employees e
WHERE salary > (
	SELECT AVG(salary)
	FROM employees
);


# 案例3:查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资
# 需要拿着这个结果去和employees进行连接
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id;

SELECT employee_id, last_name, salary, ag
FROM employees e
INNER JOIN (
	SELECT AVG(salary) ag, department_id
	FROM employees
	GROUP BY department_id
) avg_sal
ON e.`department_id` = avg_sal.department_id
WHERE e.`salary` > avg_sal.ag;


# 案例4:查询姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id, last_name
FROM employees e
WHERE department_id IN (
	SELECT DISTINCT department_id
	FROM employees
	WHERE last_name LIKE '%u%'
);


# 案例5:查询在部门的location_id为1700的部门工作的员工的员工号
SELECT employee_id
FROM employees e
WHERE department_id IN (
	SELECT department_id
	FROM departments d
	WHERE d.`location_id` = 1700

);


# 案例6:查询管理者是King的员工姓名和工资
SELECT employee_id
FROM employees e1
WHERE e1.`last_name` = 'K_ing';

SELECT last_name, salary
FROM employees e
WHERE manager_id IN (
	SELECT employee_id
	FROM employees e1
	WHERE e1.`last_name` = 'K_ing'
);

SELECT e1.last_name, e1.salary
FROM employees e1
INNER JOIN employees e2
ON e1.`manager_id` = e2.`employee_id`
WHERE e2.`last_name` = 'K_ing';

# 案例7:查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为 姓.名
SELECT CONCAT(first_name, '.', last_name) "姓.名"
FROM employees
WHERE salary = (
	SELECT MAX(salary)
	FROM employees
);

4.9 分页查询

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

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

SELECT *
FROM employees
LIMIT 5;

# 案例2:查询第11条——第25条
SELECT *
FROM employees
LIMIT 10, 15;


# 查询有奖金的员工信息,并且工资较高的前十名筛选出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary * (1 + IFNULL(commission_pct, 0)) DESC
LIMIT 10;




# 测试题
/*
已知表 stuinfo
id 学号
name 姓名
email 邮箱  john@126.com
gradeId 年级编号
sex 性别
age 年龄

已知表 grade
id 年级编号
gradeName 年级名称
*/

# 1.查询所有学院的邮箱的用户名(注:邮箱中@前面的字符)
/*
select substr(email, 1, instr(email, '@')-1) 用户名
from stuinfo;


# 2.查询男生和女生的个数
SELECT 	count(*) 个数, sex 
FROM stuinfo
group by sex;


# 3.查询年龄>18岁的所有学生的姓名和年级名称
select name, gradeName
from stuinfo s
inner join grade g
on s.gradeId = g.id
where age > 18;


# 4.查询哪个年级的学生最小年龄>20岁
select gradeId, min(age)
from stuinfo
group by gradeId
having min(age) > 20;


# 5.是说出查询语句中涉及到的所有的关键字,以及执行的先后顺序
select 查询列表          ⑦
from 表                  ①
连接类型 join 表2        ②
on 连接条件              ③
where 筛选条件           ④
group by 分组列表        ⑤
having 分组后的筛选      ⑥
order by 排序列表        ⑧
limit 偏移,条目数       ⑨
*/








# 1.查询工资最低的员工信息:last_name, salary
SELECT last_name, salary
FROM employees
WHERE salary = (
	SELECT MIN(salary)
	FROM employees
);
# 2.查询平均工资最低的部门信息
# 方式一:
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
	SELECT MIN(ag)
	FROM (
		SELECT AVG(salary) ag, department_id
		FROM employees
		GROUP BY department_id
	) ag_dep
);
SELECT d.*
FROM departments d
WHERE d.department_id = (
	SELECT department_id
	FROM employees
	GROUP BY department_id
	HAVING AVG(salary) = (
		SELECT MIN(ag)
		FROM (
			SELECT AVG(salary) ag, department_id
			FROM employees
			GROUP BY department_id
		) ag_dep
	)
);
# 方式二:
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;

SELECT d.*
FROM departments d
WHERE department_id = (
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary)
	LIMIT 1
);



# 3.查询平均工资最低的部门信息和该部门的平均工资
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;

# 方式一:使用连接查询
SELECT d.*, ag_dep.ag
FROM departments d
INNER JOIN (
	SELECT AVG(salary) ag, department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary)
	LIMIT 1
) ag_dep
ON d.`department_id` = ag_dep.department_id;

# 方式二:使用子查询(有点问题不能运行)
SELECT d.*, (
	SELECT AVG(salary) ag
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary)
	LIMIT 1
) ag_dep
FROM departments d
WHERE d.`department_id` = ag_dep.department_id;

# 4.查询平均工资最高的 job 的信息
SELECT j.*
FROM jobs j
WHERE j.`job_id` = (
	SELECT job_id
	FROM employees
	GROUP BY job_id
	ORDER BY AVG(salary) DESC
	LIMIT 1
);
# 5.查询平均工资高于公司平均工资的部门有哪些
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) > (
	SELECT AVG(salary)
	FROM employees
);
# 6.查询出公司中所有manager的详细信息
SELECT *
FROM employees
WHERE employee_id IN (
	SELECT DISTINCT manager_id
	FROM employees
);
# 7.各个部门中 最高工资中最低的那个部门的最低工资是多少
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY MAX(salary)
LIMIT 1;

SELECT MIN(salary), department_id
FROM employees
WHERE department_id = (
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY MAX(salary)
	LIMIT 1
);

# 8.查询平均工资最高的部门的manager的详细信息:last_name、department_id、email、salary

SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id = (
	SELECT manager_id
	FROM departments
	WHERE department_id = (
		SELECT department_id
		FROM employees
		GROUP BY department_id
		ORDER BY AVG(salary) DESC
		LIMIT 1
	)
);

SELECT last_name, d.department_id, email, salary
FROM employees e
INNER JOIN departments d
ON e.`employee_id` = d.`manager_id`
WHERE d.`department_id` = (
		SELECT department_id
		FROM employees
		GROUP BY department_id
		ORDER BY AVG(salary) DESC
		LIMIT 1
);

4.10 union联合查询

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

语法:
查询语句1
union
查询语句2
union 
...

应用场景:
要查询的结果来自于多个表,且多个表之间没有直接的连接关系,但查询的信息一致时

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

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

SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id > 90;

# 案例:查询中国用户中男性信息以及外国用户中男性的用户信息
/*
select id, cname, csex from t_ca where csex = '男'
union all
select t_id, tName, tGender from t_ua where tGender = 'male'; 
*/

5. DML语言的学习

5.1 插入语句

5.2 修改语句

5.3 删除语句

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


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

SELECT * FROM beauty;
# 1.插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty(id, NAME, sex, borndate, phone, photo, boyfriend_id)
VALUE(13, '唐艺昕', '女', '1990-4-23', '198888888', NULL, 2);


# 2.不可为null的列必须插入值,可以为null的列如何插入值?
#方式一:
INSERT INTO beauty(id, NAME, sex, borndate, phone, photo, boyfriend_id)
VALUE(13, '唐艺昕', '女', '1990-4-23', '198888888', NULL, 2);

#方式二:
INSERT INTO beauty(id, NAME, sex, borndate, phone, boyfriend_id)
VALUE(14, '金星', '女', '1990-4-23', '138888888', 9);


# 3.列的顺序可以调换
INSERT INTO beauty(NAME, sex, id, phone)
VALUES('蒋欣', '女', 15, '110');


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


# 方式二:
/*
语法:
insert into 表名
set 列名=值, 列名=值, ...
*/

INSERT INTO beauty
SET id = 19, NAME = '刘涛', phone = '999';


# 两种方式大pk
# 1.方式一支持插入多行,方式二不支持
INSERT INTO beauty
VALUES(17, '张三', '男', NULL, '119', NULL, NULL),
(18, '李四', '女', '1990-4-23', '198888888', NULL, 2);

# 2.方式一支持子查询,方式二不支持
/*
insert into beauty(id, name, phone)
select id, boyname, '123546546'
from boys where id < 3;
*/




# 二、修改语句
/*
1.修改单表的记录

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

2.修改多表的记录

语法:
sql92语法:
update 表1 别名, 表2 别名
set 列=值, ...
where 连接条件
and 筛选条件

sql99语法:
update 表1 别名
inner|left|right join 表2 别名
on 连接条件
set 列 = 值, ......
where 筛选条件

*/

SELECT * FROM beauty;
# 1.修改单表记录
# 案例1:修改beauty表中姓张的女生的电话为1389797
UPDATE beauty SET phone = '1389797'
WHERE NAME LIKE '张%';

SELECT * FROM boys;
# 案例2:修改boys表中id号为2的名称为张飞,魅力值为10
UPDATE boys SET boyname = '张飞', usercp = 10
WHERE id = 2;

# 案例3:搭配limit使用
DELETE FROM beauty
WHERE boyfriend_id = 4
LIMIT 1;


UPDATE boys SET boyName = '张无忌', usercp = 100
WHERE id = 1;
UPDATE boys SET boyName = '张飞', usercp = 10
WHERE id = 2;
UPDATE boys SET boyName = '黄晓明', usercp = 50
WHERE id = 3;
UPDATE boys SET boyName = '段誉', usercp = 300
WHERE id = 4;


# 2.修改多表记录
# 案例1:修改张无忌的女朋友的手机号为114
UPDATE boys bo
INNER JOIN beauty b
ON b.`boyfriend_id` = bo.`id`
SET b.`phone` = '114'
WHERE bo.`boyName` = '张无忌';

# 案例2:修改没有男朋友的女生的男朋友编号都为2号(注意:is null和= null不一样)
UPDATE beauty b
LEFT JOIN boys bo ON b.`boyfriend_id` = bo.`id`
SET b.`boyfriend_id` = 2
WHERE bo.id IS NULL;

# 三、删除语句
/*
方式一:delete
语法:
1.单表的删除
delete from 表名 where 筛选条件

2.多表的删除

sql92语法:
delete 表1的别名,表2的别名
from 表1 别名,表2 别名 
where 连接条件
and 筛选条件;


sql99语法
delete 表1的别名,表2的别名
from 表1 别名
inner|left|right join 表2 别名
on 连接条件
where 筛选条件;

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

# 方式一:delete
# 1.单表的删除
# 案例1:删除手机编号最后一位为9的女生信息
DELETE FROM beauty WHERE phone LIKE '%9';
SELECT * FROM beauty;


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


SELECT * FROM boys;
# 案例2:删除黄晓明的信息以及他女朋友的信息
DELETE b, bo
FROM beauty b
INNER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`boyName` = '黄晓明';


# 方式二:truncate语句
# 案例:将魅力值>100的男生信息删除
TRUNCATE TABLE boys;


# delete pk truncate
/*
1.delete 可以加 WHERE 条件,TRUNCATE 不能加
2.使用truncate删除效率更高
3.加入要删除的表中有自增长列,如果用delete删除,再插入数据,自增长列的值从断点开始
而truncate删除后,再插入数据,自增长列的值从1开始
4.truncate删除没有返回值(显示的是0行受影响),delete删除有返回值
5.truncate删除不能回滚,delete删除可以回滚

*/


SELECT * FROM boys;
DELETE FROM boys;
INSERT INTO boys (boyname, usercp)
VALUES('张飞', 100), ('刘备', 100), ('关云长', 100)
# 1. 运行以下脚本创建表my_employees

CREATE TABLE my_employees(
	Id INT(10),
	First_name VARCHAR(10),
	Last_name VARCHAR(10),
	Userid VARCHAR(10),
	Salary DOUBLE(10, 2)
);

CREATE TABLE users(
	id INT,
	userid VARCHAR(10),
	department_id INT
);


# 2.显示表my_employees的结构
DESC my_employees;

# 3.向my_employees表中插入数据
#方式一:
INSERT INTO my_employees
VALUES(1, 'patel', 'Ralph', 'Rpatel', 895),
(2, 'Dancs', 'Betty', 'Bdancs', 860),
(3, 'Biri', 'Ben', 'Bbiri', 1100),
(4, 'Newman', 'Chad', 'Cnewman', 750),
(5, 'Ropeburn', 'Audrey', 'Aropebur', 1550);

DELETE FROM my_employees;

#方式二:
INSERT INTO my_employees
SELECT 1, 'patel', 'Ralph', 'Rpatel', 895 UNION
SELECT 2, 'Dancs', 'Betty', 'Bdancs', 860 UNION
SELECT 3, 'Biri', 'Ben', 'Bbiri', 1100 UNION
SELECT 4, 'Newman', 'Chad', 'Cnewman', 750 UNION
SELECT 5, 'Ropeburn', 'Audrey', 'Aropebur', 1550;


# 4.向users表中插入数据
INSERT INTO users
VALUES(1, 'Rpatel', 10),
(2, 'Bdancs', 10),
(3, 'Bbiri', 20),
(4, 'Cnewman', 30),
(5, 'Aropebur', 40);


# 5.3号员工的last_name修改为”drelxer“
UPDATE my_employees SET last_name = 'drelxer' WHERE id = 3;


# 6.将所有工资少于900的员工的工资修改为1000
UPDATE my_employees SET salary = 1000 WHERE salary < 900;


# 7.将userid为Bbiri的user表和my_employees表的记录全部删除
DELETE u, e
FROM users u
INNER JOIN my_employees e
ON u.userid = e.userid
WHERE u.userid = 'Bbiri';


# 8.删除所有数据
DELETE FROM my_employees;
DELETE FROM users;


# 9.检查所作的修正
SELECT * FROM users;
SELECT * FROM my_employees;


# 10.清空表my_employees
TRUNCATE TABLE my_employees;

6. DDL语言的学习

6.1 库和表的管理

# DDL
/*
数据定义语言

库和表的管理

一、库的管理
创建、修改、删除

二、表的管理
创建、修改、删除

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

*/

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

# 案例:创建库Books
CREATE DATABASE IF NOT EXISTS books;


# 2.库的修改(直接在文件中改)


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


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


# 二、表的管理
# 1.表的创建
/*
create table 表名(
	列名 列的类型【(长度) 约束】,
	列名 列的类型【(长度) 约束】,
	列名 列的类型【(长度) 约束】,
	...
	列名 列的类型【(长度) 约束】
)
*/

# 案例1:创建表Book
CREATE TABLE IF NOT EXISTS book (
	id INT,  #编号
	bName VARCHAR(20),   #书名
	price DOUBLE,        #价格
	authorId INT,        #作者
	publishDate DATETIME #出版日期
	
);

DESC book;


# 案例2:创建表author
CREATE TABLE author(
	id INT,
	au_name VARCHAR(20),
	nation VARCHAR(10)
);
DESC author;


# 2.表的修改
/*
alter table 表名 add|drop|modify|change column 列名 【列类型 约束】;
alter table 表名 add column 列名 类型 【first|after 字段名】;

*/
# ①修改列名
ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME;
# ②修改列的类型和约束
ALTER TABLE book MODIFY COLUMN pubDate TIMESTAMP;
# ③添加新列
ALTER TABLE author ADD COLUMN annual DOUBLE;
ALTER TABLE test_add_column ADD COLUMN newT INT FIRST;
ALTER TABLE test_add_column ADD COLUMN newT INT AFTER t2;
# ④删除列
ALTER TABLE author DROP COLUMN annual;
# ⑤修改表名
ALTER TABLE author RENAME TO book_author;


# 3.表的删除
DROP TABLE IF EXISTS book_author;
SHOW TABLES;


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

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


ALTER DATABASE books CHARACTER SET gbk;
DELETE FROM author;
# 4.表的复制
INSERT INTO author 
VALUES(1, '村上春树', '日本'),
(2, '莫言', '中国'),
(3, '冯唐', '中国'),
(4, '金庸', '中国');

SELECT * FROM author;


# 1.仅仅复制表的结构
CREATE TABLE copy LIKE author;
# 2.复制表的结构+数据
CREATE TABLE copy2
SELECT * FROM author;
SELECT * FROM copy2;

# 3.只复制部分数据
CREATE TABLE copy3
SELECT id, au_name
FROM author
WHERE id BETWEEN 2 AND 4;
SELECT * FROM copy3;


# 4.仅仅复制部分结构
CREATE TABLE copy4
SELECT id, au_name
FROM author
WHERE 0;

DESC copy4;
SELECT * FROM copy4;
# 1.创建表
USE test;
CREATE TABLE dept1(
	id INT(7),
	NAME VARCHAR(25)
);

# 2.将表departments的数据插入到表dept2中
CREATE TABLE dept2
SELECT department_id, department_name
FROM myemployees.`departments`;

SELECT * FROM dept2;


# 3.创建表
CREATE TABLE emp5(
	id INT(7),
	first_name VARCHAR(25),
	last_name VARCHAR(25),
	dept_id INT(7)
);


# 4.将last_name的长度增加为50
ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);

# 5.根据表employees创建employees2
CREATE TABLE employees2 LIKE myemployees.`employees`;

# 6.删除表emp5中的数据
TRUNCATE TABLE emp5;

# 7.删除表emp5
DROP TABLE emp5;


# 8.将表employees2重名为emp5
ALTER TABLE employees2 RENAME TO emp5;


# 9.在表dept和emp5中添加新列test_column,并检查所作的操作
ALTER TABLE dept1 ADD COLUMN test_column INT;
DESC dept1;

# 10.直接删除表emp5中的列dept_id
ALTER TABLE emp5 DROP COLUMN job_id;
DESC emp5;

6.2 常见数据类型介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

*/

# 一、整型
/*
tinyint、smallint、mediumint、int、bigint
1        2         3          4    8

特点:
1.如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned
2.如果插入的数据超出了整型范围,则默认插入临界值
3.如果不设置长度,会有默认的长度(长度代表了显示的最大宽度,如果不够会用0填充,但必须搭配zerofill使用)

*/

# 1.设置无符号和有符号
CREATE TABLE tab_int(
	t1 INT(7) ZEROFILL,   #默认变成无符号
	t2 INT UNSIGNED
)

# 二、小数
/*
1.浮点型
float(M, D)
double(M, D)
2.定点型
dec(M, D)
decimal(M, D)

特点
1.M:整数部位+小数部位
D:小数部位
如果超过范围,则插入临界值

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

3.定点型的精确度较高,如果要求插入的数值精度较高则优先使用,其余使用float和double

*/

# 测试M和D
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.456, 123.456, 123.456);

/*
原则:所选择的类型越简单越好,能保存数值的类型越小越好
*/


# 三、字符类型
/*
较短的文本:char、varchar、binary和varbinary(用于保存较短的二进制)、enum(用于保存枚举)、set(用于保存集合)
较长的文本:text、blob(较大的二进制)

特点:
	写法		M的意思				特点			空间耗费	效率
char 	char(M)		最大的字符数,可以省略默认为1	固定长度的字符		比较耗费	高
varchar	varchar(M)	最大的字符数,不可以省略	可变长度的字符		比较节省	低
*/

CREATE TABLE tab_char(
	c1 ENUM('a', 'b', 'c')
);

INSERT INTO tab_char VALUES('a');
INSERT INTO tab_char VALUES('b');
INSERT INTO tab_char VALUES('c');
INSERT INTO tab_char VALUES('m');
INSERT INTO tab_char VALUES('A');

SELECT * FROM tab_char;


CREATE TABLE tab_set(
	c1 SET('a', 'b', 'c', 'd')
);

INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('a,b');
INSERT INTO tab_set VALUES('a,c,d');

SELECT * FROM tab_set;


# 四、日期型
/*
分类:
date 只保存日期
time 只保存时间
year 只保存年

datetime 保存日期+时间
timestamp 保存日期+时间

特点:
		字节		范围		时区等的影响
datetime	8		1000-9999	不受
timestamp	4		1970-2038	受
*/
CREATE TABLE tab_date(
	t1 DATETIME,
	t2 TIMESTAMP
);

INSERT INTO tab_date VALUES(NOW(), NOW());
SELECT * FROM tab_date;
SHOW VARIABLES LIKE 'time_zone';
SET time_zone = '+9:00';

6.3 常见约束

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

分类:六大约束
	NOT NULL:非空,用于保证该字段的值不能为空(比如:姓名、学号等)
	DEFAULT:默认,用于保证该字段有默认值(比如:性别)
	PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空(比如:学号、员工编号等)
	UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空(比如:座位号)
	CHECK:检查约束【mysql不支持】(比如:年龄、性别)
	FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值
		在从表添加外键约束,用于引用主表中某列的值
	比如学生表的专业编号,员工表的部门编号,员工表的工种编号
	
	
添加约束的时机:
	1.创建表时
	2.修改表时
	
约束的添加分类:
	列级约束:六大约束语法上都支持,但外键约束没有效果
	表级约束:除了非空、默认,其他的都支持
	
	
主键和唯一的大对比:
			保证唯一性	是否允许为空		一个表中可以有几个		是否允许组合
	主键		√		×			  一个				允许但不推荐
	唯一		√		√			  可以有多个			允许但不推荐
	
外键:
	1.在从表上设置外键关系
	2.从表的外键列的类型和主表的关联列的类型要求一致或兼容
	3.要求主表中的关联列必须是一个键(主键、唯一键)
	4.要求插入数据时应该先插入主表再插入从表,删除时应该先插入从表再插入主表
	5.一个列可以创建多个约束,用空格隔开即可

CREATE TABLE 表名(
	字段名 字段类型 列级约束,
	字段名 字段类型,
	表级约束
)
*/

# 一、创建表时添加约束
# 1.添加列级约束
/*
语法:

直接在字段名和类型后面追加约束类型
只支持:默认、非空、主键、唯一
*/
CREATE DATABASE students;
USE students;

CREATE TABLE IF NOT EXISTS stuinfo(
	id INT PRIMARY KEY,                 #主键
	stuName VARCHAR(20) NOT NULL,       #非空
	gender CHAR(1) CHECK(gender = '男' OR gender = '女'),       #检查
	seat INT UNIQUE,                    #唯一
	age INT DEFAULT 18,                 #默认约束
	majorId INT REFERENCES major(id)      #外键
	
);


CREATE TABLE IF NOT EXISTS major(
	id INT PRIMARY KEY,
	marjorName VARCHAR(20)
);

DESC stuinfo;
#查看stuinfo表中所有的索引,包括主键、外键、唯一
SHOW INDEX FROM stuinfo;


# 2.添加表级约束
/*
语法:在各字段的最下面
【constraint 约束名】 约束类型(字段名)
*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuName VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorId INT,
	
	CONSTRAINT pk PRIMARY KEY(id, stuName), #主键
	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 IF NOT EXISTS stuinfo(
	id INT PRIMARY KEY,
	stuname VARCHAR(20) NOT NULL,
	sex CHAR(1),
	age INT DEFAULT 18,
	seat INT UNIQUE,
	majorid INT,
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)

);


DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuName VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorId INT
);

# 二、修改表时添加约束
/*
1.添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;
2.添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
*/
# 1.添加非空约束
DESC stuinfo;
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NOT NULL;
# 2.添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
# 3.添加主键
# ①列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
# ②表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

# 4.添加唯一
# ①列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
# ②表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);

# 5.添加外键
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id);


# 三、修改表时删除约束
# 1.删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20);
# 2.删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT;
# 3.删除主键
ALTER TABLE stuinfo MODIFY COLUMN id INT;
ALTER TABLE stuinfo DROP PRIMARY KEY;
# 4.删除唯一键
ALTER TABLE stuinfo DROP INDEX seat;
# 5.删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;

SHOW INDEX FROM stuinfo;


#可以通过以下两种方式来删除主表的记录
#方式一:级联删除
ALTER TABLE stuinfo ADD CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE CASCADE;

#方式二:级联置空
ALTER TABLE stuinfo ADD CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE SET NULL;
# 标识列
/*
又称为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值


特点:
1.标识列不一定和主键搭配,但要求是一个key
2.一个表只能有一个标识列
3.标识列的类型只能是数值型
4.标识列可以通过SET auto_increment_increment = 1;设置步长,可以通过手动插入值设置起始值
*/

# 一、创建表时设置标识列
DROP TABLE IF EXISTS tab_identity;
CREATE TABLE tab_identity(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);
SELECT * FROM tab_identity;


TRUNCATE TABLE tab_identity;
INSERT INTO tab_identity VALUES(NULL, 'john');
INSERT INTO tab_identity(NAME) VALUES('licy');

SHOW VARIABLES LIKE '%auto_increment%';

# 设置步长
SET auto_increment_increment = 1;

# 二、修改表时设置标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;


# 三、修改表示删除标识列 
ALTER TABLE tab_identity MODIFY COLUMN id INT;

7. TCL语言的学习

7.1 事务和事务处理

# TCL
/*
Transaction Control Language 事务控制语言

事务:
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行

案例:转账
张三丰 1000
郭襄   1000

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


事务的特性:
ACID
原子性:一个事务不可以再分割,要么都执行要么都不执行
一致性:一个事务执行会使数据从一个一致状态切换到另一个一致状态
隔离性:一个事务的执行不受其他事务的干扰
持久性:一个事务一旦提交,则会永久改变数据库的数据


事务的创建
隐式事务:事务没有明显的开启和结束标记(比如insert、update、delete语句)
显示事务:事物具有明显的开启和结束标记
前提:必须先设置自动提交功能为禁用

事务的隔离级别:
read uncommitted:出现脏读、幻读、不可重复读
read committed:避免脏读,出现幻读和不可重复读
repeatable read:避免脏读、不可重复读,出现幻读
serializable:避免脏读、幻读、不可重复读

mysql中默认第三个级别 repeatable read
oracle中默认第二个级别 read committed

查看当前的隔离级别:select @@tx_isolation
设置当前mysql连接的隔离级别:set transaction isolation level read committed
设置数据库系统的全局的隔离级别:set global transaction isolation level read committed


set autocommit = 0;

步骤1:开启事务
set autocommit = 0;
start transaction; 可选的
步骤2:编写事务中的sql语句(select insert update delete)
语句1;
语句2;
......
步骤3:结束事务
commit; 提交事务
rollback; 回滚事务
savepoint 节点名; 设置保存点



*/

# 查看存储引擎
SHOW ENGINES;


# 演示事务的使用步骤
DROP TABLE IF EXISTS account;

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

TRUNCATE TABLE account;

INSERT INTO account(username, balance)
VALUES('zhang', 1000),
('zhao', 1000);

SELECT * FROM account;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
# 编写一组事务的语句
UPDATE account SET balance = 1000 WHERE username = 'zhang';
UPDATE account SET balance = 1000 WHERE username = 'zhao';
# 结束事务
ROLLBACK;
COMMIT;


# 演示savepoint的使用
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account WHERE id = 2;
SAVEPOINT a;                        #设置保存点
DELETE FROM account WHERE id = 1;
ROLLBACK TO a;                      #回滚到保存点



在这里插入图片描述

8. 视图的讲解

#视图
/*
含义:虚拟表,和普通表一样使用
mysql5.1版本出现的新特性,是通过动态表生成的数据

比如:舞蹈班和普通班级的对比

	创建语法的关键字	是否实际占用物理空间		使用
视图	create view		只是保存了sql逻辑		增删改查,一般不能增删改
	
表	create table		保存了数据			增删改查

*/

# 案例1:查询姓张的学生名和专业名
SELECT stuName, marjorName
FROM stuinfo s
INNER JOIN major m
ON s.`majorId` = m.`id`
WHERE stuName LIKE '张%';

SELECT * FROM stuinfo;

# 封装成视图
CREATE VIEW v1
AS
SELECT stuName, marjorName
FROM stuinfo s
INNER JOIN major m
ON s.`majorId` = m.`id`;

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



# 一、创建视图
/*
语法:
create view 视图名
as
查询语句;
*/
# 案例1:查询姓名中中包含a字符的员工名、部门名和工种信息

CREATE VIEW v3
AS
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` = j.`job_id`;

SELECT * FROM v3 WHERE last_name LIKE '%a%';


# 案例2:查询各部门的平均工资级别
# 方式一:子查询
SELECT ag_dep.*, g.grade_level
FROM (
	SELECT AVG(salary) ag, department_id
	FROM employees
	GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;

# 方式二:视图
CREATE VIEW v4
AS
SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id;

SELECT v4.ag, g.grade_level
FROM v4
INNER JOIN job_grades g
ON v4.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;


# 案例三:查询平均工资最低的部门信息
# 方式一:
SELECT * 
FROM departments d
WHERE department_id = (
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary)
	LIMIT 1
);

# 方式二:
SELECT * FROM v4 ORDER BY ag LIMIT 1;


# 案例四:查询平均工资最低的部门名和工资
CREATE VIEW v5
AS
SELECT * FROM v4 ORDER BY ag LIMIT 1;

SELECT * FROM v5;

SELECT department_name, ag
FROM departments d
INNER JOIN v5
ON v5.department_id = d.`department_id`;


# 二、视图的修改
# 方式一:
/*
create or replace view 视图名
as
查询语句;	
*/
SELECT * FROM v5;

CREATE OR REPLACE VIEW v5
AS
SELECT AVG(salary), job_id
FROM employees
GROUP BY job_id;

# 方式二:
/*
语法:
alter view 视图名
as
查询语句;

*/

ALTER VIEW v5
AS
SELECT * FROM employees;


# 三、删除视图
/*
语法:drop view 视图名, 视图名, 视图名....;
*/
DROP VIEW v2, v3, v4, v5;


# 四、查看视图
DESC v3;
SHOW CREATE VIEW v3;


# 案例1:创建视图emp_v1,要求查询电话号码以'011'开头的员工姓名和工资、邮箱
CREATE VIEW emp_v1
AS
SELECT last_name, salary, email
FROM employees e
WHERE phone_number LIKE '011%';

SELECT * FROM emp_v1;


# 案例2:创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW emp_v2
AS
SELECT MAX(salary) mx_dep, department_id
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 12000;

SELECT d.*, m.mx_dep
FROM departments d
JOIN emp_v2 m
ON m.department_id = d.`department_id`;


DROP VIEW emp_v1, emp_v2;
# 五、视图的更新
CREATE OR REPLACE VIEW myv1
AS
SELECT last_name, email
FROM employees;

SELECT * FROM myv1;

# 1.插入
INSERT INTO myv1 VALUES('aa', 'zd@qq.com');
# 2.修改
UPDATE myv1 SET last_name = 'ss' WHERE last_name = 'aa';
# 3.删除
DELETE FROM myv1 WHERE last_name = 'ss';

# 具备以下特点的视图不允许更新
# 1. 包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all
CREATE OR REPLACE VIEW myv1
AS
SELECT MAX(salary) m, department_id
FROM employees
GROUP BY department_id;

# 更新
UPDATE myv1 SET m = 9000 WHERE department_id = 10;

# 2.常量视图
CREATE OR REPLACE VIEW myv2
AS
SELECT 'john' NAME;

SELECT * FROM myv2;

# 更新
UPDATE myv2 SET NAME = 'lucy';

# 3.select中包含子查询
# 4.join
# 5.from一个不能更新的视图
# 6.where子句的子查询引用了from子句中的表


# 2.delete和truncate在事务使用时的区别
# 演示delete
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;

SELECT * FROM account;
# 演示truncate
SET autocommit = 0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;


# 案例1:创建book表,字段如下
/*
bid整型,要求主键
bname字符型,要求设置唯一键,并非空
price浮点型,要求有默认值 10
btypeId类型编号,要求引用bookType表的id字段
*/

CREATE TABLE IF NOT EXISTS book(
	bid INT PRIMARY KEY,
	bname VARCHAR(20) UNIQUE NOT NULL,
	price DOUBLE DEFAULT 10,
	btypeId INT
	
	#constraint fk_book_booktype foreign key(btypeId) references bookType(id)

);

# 案例2:开启事务,向表中插入1行数据,并结束
SET autocommit = 0;
START TRANSACTION;
INSERT INTO book VALUES(1, 'xiaolifeidao', 100, 1);
COMMIT;

SELECT * FROM book;

# 案例3:创建视图,实现查询价格>100的书名和类型名
CREATE VIEW myv1
AS
SELECT bname, NAME
FROM book b
INNER JOIN bookType t
ON b.btypeId = t.id
WHERE price > 100;

# 案例4:修改视图,实现查询价格在90-120之间的书名和价格
CREATE OR REPLACE VIEW myv1
AS
SELECT bname, price 
FROM book
WHERE price BETWEEN 90 AND 120;

# 案例5:删除刚刚创建的视图
DROP VIEW myv1;



9. 变量

# 变量
/*
系统变量:
	全局变量
	会话变量
自定义变量:
	用户变量
	局部变量

*/

# 一、系统变量
/*
说明:变量由系统提供,不是用户定义,属于服务器层面
使用的语法:
1.查看所有的系统变量
show global|【session】 variables;
2.查看满足条件的部分系统变量
show global|【session】 variables like '%char%';
3.查看指定的某个系统变量的值
select @@global|【session.】系统变量名;
4.为某个系统变量赋值
方式一:
set global|【session】 系统变量名 = 值;
方式二:
set global|【session.】系统变量名 = 值;

注意:如果是全局级别,则需要加global,如果是会话级别,则需要加session,如果不写,则默认session
*/


# 一、全局变量
/*
作用域:服务器每次启动将为所有的全局变量赋初始值,针对所有会话(连接)有效,但不能跨重启
*/
# 1.查看所有的全局变量
SHOW GLOBAL VARIABLES;
# 2.查看部分的全局变量
SHOW GLOBAL VARIABLES LIKE '%char%';

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

# 4.为某个指定的全局变量赋值
SELECT @@global.autocommit = 0;


# 二、会话变量
/*
作用域:仅仅针对于当前会话(连接)有效
*/
# 1.查看所有的会话变量
SHOW SESSION VARIABLES;
SHOW VARIABLES;
# 2.查看部分的会话变量
SHOW SESSION VARIABLES LIKE '%char%'; 
SHOW VARIABLES LIKE '%char%'; 
# 3.查看指定的会话变量
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
# 4.为某个会话变量赋值
SET @@session.tx_isolation = 'read-uncommitted';
SET SESSION tx_isolation = 'read-committed';


# 二、自定义变量
/*
说明:变量是用户自定义的,不是系统的
使用步骤:
声明
赋值
使用(查看、比较、运算等)
*/

# 1.用户变量
/*
作用域:针对于当前会话(连接)有效,同于会话变量的作用域
应用在任何地方,也就是begin end 里面或外面都可以
*/
# 声明并初始化
SET @用户变量名:=;
SET @用户变量名=;
SELECT @用户变量名:=;


# 赋值或更新用户变量值
方式一:用过SET或SELECT
	SET @用户变量名:=;
	SET @用户变量名=;
	SELECT @用户变量名:=;
方式二:通过SELECT INTO
	SET 字段 INTO 变量名
	FROM 表;
	
	
# 使用(查看用户变量的值)
SELECT @用户变量名;
	
# 案例:
# 声明并初始化
SET @name = 'john';
SET @name = 100;
SET @count = 1;


# 赋值
SELECT COUNT(*) INTO @count
FROM employees;


# 查看
SELECT @count;



# 三、局部变量
/*
作用域:仅仅在定义他的begin end中有效
引用在begin end中的第一句话
*/
# 1.声明
DECLARE 变量名 类型;
DECLARE 变量名 类型 DEFAULT 值;

# 2.赋值
方式一:用过SET或SELECT
	SET 用户变量名:=;
	SET 用户变量名=;
	SELECT @用户变量名:=;
方式二:通过SELECT INTO
	SET 字段 INTO 变量名
	FROM 表;

# 3.使用
SELECT 局部变量名;



/*
		作用域		定义和使用的位置		语法
用户变量:	当前会话	会话中的任何地方		必须加@符号
局部变量:	begin end中	只能在begin end的第一句话	一般不用加@符号
*/

# 1.用户变量
SET @m = 1;
SET @n = 2;
SET @sum = @m + @n;
SELECT @sum;


# 2.局部变量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE SUM INT;
SET SUM = m + n;

10. 存储过程和函数

10.1 存储过程

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

*/

# 一、创建语法
/*
create procedure 存储过程名(参数列表)
begin
	存储过程体(一组合法的sql语句)
end
注意:
1.参数列表包含三部分
参数模式  参数名  参数类型
举例:
in stuname varchar(20)

参数模式:
in:该参数可以作为输入,该参数需要调用方传入值
out:该参数可以作为输出,该参数可以作为返回值
inout:既作为传入值,也作为传出值

2.如果存储过程体仅仅只有一句话,begin和end可以省略
存储过程体中的每一条sql语句结尾必须加分号
存储过程的结尾可以使用delimiter重新设置
语法:
delimiter 结束标记
案例:
delimiter $
*/

# 二、调用语法
CALL 存储过程名(实参列表);


# 1.空参列表
# 案例:插入到admin表中五条数据
SELECT * FROM admin;

DELIMITER $
CREATE PROCEDURE mypl()
BEGIN
	INSERT INTO admin(username, `password`) VALUES('john1', '0000'),
	('john2', '0000'),('john3', '0000'),('john4', '0000');
END $

# 调用
CALL myp1() $

# 2.创建带in模式参数的存储过程
# 案例1:创建存储过程,实现根据女神名查看对应的男神信息
CREATE PROCEDURE myp2(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 $

# 调用
CALL myp2('柳岩') $


# 案例2:创建存储过程实现,用户是否登陆成功
CREATE PROCEDURE myp3(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 $

CREATE PROCEDURE myp4(IN username VARCHAR(20), IN PASSWORD VARCHAR(20))
BEGIN
	DECLARE result INT DEFAULT 0;  #声明并初始化
	
	SELECT COUNT(*) INTO result             #给变量赋值
	FROM admin
	WHERE admin.username = username
	AND admin.`password` = PASSWORD;
	
	SELECT IF(result > 0, '成功', '失败');                          #查看

END $

# 调用
CALL myp3('张飞', '8888') $


# 3.创建带out模式的存储过程
# 案例1:根据传入的女神名,传出对应的男神名
CREATE PROCEDURE myp5(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$


# 案例2:根据女神名,返回对应的男生名和男生魅力值
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('小昭', @bName, @userCP)$
SELECT @bName, @userCP$


# 4.创建带inout模式参数的存储过程
# 案例1:传入a和b两个值,最终a和b都翻倍并返回
SET @m = 10$
SET @n = 20$
CREATE PROCEDURE myp8(INOUT a INT, INOUT b INT)
BEGIN
	SET a = a * 2;
	SET b = b * 2;
END $
CALL myp8(@m, @n)$
SELECT @m, @n$



# 案例1:创建存储过程实现传入用户名和密码,插入到admin表中
CREATE PROCEDURE test_pro1(IN username VARCHAR(20), IN loginPwd VARCHAR(20))
BEGIN
	INSERT INTO admin(admin.`username`, admin.`password`)
	VALUES(username, loginPwd);

END $

CALL test_pro1('admin', '0000')$
SELECT * FROM admin$

# 案例2:创建存储过程实现传入女生编号,返回女生名和女生电话
CREATE PROCEDURE test_pro2(IN id INT, OUT beautyName VARCHAR(20), OUT phone VARCHAR(20))
BEGIN
	SELECT beauty.`name`, beauty.`phone` INTO beautyName, phone
	FROM beauty
	WHERE beauty.`id` = id;
END $

CALL test_pro2(1, @n, @p)$
SELECT @n, @p$

# 案例3:创建存储过程实现传入两个女生生日,返回大小
CREATE PROCEDURE test_pro3(IN birth1 DATETIME, IN birth2 DATETIME, OUT result INT)
BEGIN
	SELECT DATEDIFF(birth1, birth2) INTO result;
END $
CALL test_pro3('1998-1-1', NOW(), @result)$
SELECT @result$


# 二、删除存储过程
# 语法:drop procedure 存储过程名
DROP PROCEDURE p1;


# 三、查看存储过程的信息
SHOW CREATE PROCEDURE myp2;


# 案例4:创建存储过程实现传入一个日期,格式化成xx年xx月xx日并返回
CREATE PROCEDURE test_pro4(IN mydate DATETIME, OUT strDate VARCHAR(50))
BEGIN
	SELECT DATE_FORMAT(mydate, '%y年%m月%d日') INTO strDate;
END $
CALL test_pro4(NOW(), @str)$
SELECT @str$


# 案例5:创建存储过程实现传入女神名称,返回女神 and 男神 格式的字符串
CREATE PROCEDURE test_pro5(IN beautyName VARCHAR(20), OUT str VARCHAR(50))
BEGIN
	SELECT CONCAT(beautyName, ' and ', IFNULL(boyName, 'null')) INTO str
	FROM boys bo
	RIGHT JOIN beauty b ON b.boyfriend_id = bo.id
	WHERE b.name = beautyName;
END $



10.2 函数

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

区别:
存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新
函数:有且仅有一个返回,适合做数据处理后返回一个结果
*/


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

注意:
1.参数列表 包含两部分:
参数名 参数类型

2.函数体:肯定会有return语句,如果没有会报错
3.函数体中仅有一句话,则可以省略begin end
4.使用delimiter语句设置结束标记

*/

# 二、调用语法
SELECT 函数名(参数列表)




# 1.无参有返回
# 案例1:返回公司的员工个数
CREATE FUNCTION myf1() RETURNS INT
BEGIN
	DECLARE c INT DEFAULT 0;      # 定义变量
	SELECT COUNT(*) INTO c        # 为变量赋值
	FROM employees;
	
	RETURN c;
END $

SELECT myf1()$

# 2.有参有返回
# 案例1:根据员工名返回工资
CREATE FUNCTION myf2(ename VARCHAR(20)) RETURNS DOUBLE
BEGIN
	DECLARE s DOUBLE DEFAULT 0;
	SELECT salary INTO s
	FROM employees e
	WHERE e.last_name = ename;
	
	RETURN s;

END $

SELECT myf2('kochhar')$

# 案例2:根据部门名返回该部门的平均工资
CREATE FUNCTION myf3(dname VARCHAR(20)) RETURNS DOUBLE
BEGIN
	DECLARE sal DOUBLE;
	SELECT AVG(salary) INTO sal
	FROM employees e
	INNER JOIN departments d
	ON e. department_id = d.department_id
	WHERE d.department_name = dname;
	
	RETURN sal;
END $

SELECT myf3('IT')$


# 三、查看函数
SHOW CREATE FUNCTION myf3;


# 四、删除函数
DROP FUNCTION myf3;


# 案例1:创建函数,实现传入两个float,返回两者之和
CREATE FUNCTION test_fun1(num1 FLOAT, num2 FLOAT) RETURNS FLOAT
BEGIN
	DECLARE SUM FLOAT DEFAULT 0;
	SET SUM = num1 + num2;
	RETURN SUM;
END $

SELECT test_fun1(1, 2)$

11. 流程控制结构

在这里插入图片描述

# 流程控制结构
/*
顺序结构:程序从上向下执行
分支结构:程序从两条或多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码

*/

# 一、分支结构
# 1.if结构
/*
功能:实现简单的双分支
语法:
if(表达式1, 表达式2, 表达式3)
*/


# 2.case结构
/*
情况1:类似于switch语句,一般用于实现等值判断
语法:
	case 变量|表达式|字段
	when 要判断的值1 then 返回的值1或语句1;
	when 要判断的值2 then 返回的值1或语句2;
	when 要判断的值3 then 返回的值1或语句3;
	...
	else 要返回的值n或语句n;
	end case;
	
情况2:类似于多重if语句,一般用于实现区间判断
	case 
	when 要判断的条件1 then 返回的值1或语句1;
	when 要判断的条件2 then 返回的值1或语句2;
	when 要判断的条件3 then 返回的值1或语句3;
	...
	else 要返回的值n或语句n;
	end case;
	
特点:
1.可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或begin end的外面
可以作为独立的语句去使用,只能放在begin end中

2.else可以省略,如果else省略且所有when不成立,则返回null

*/

# 案例1:创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示A,80-90,显示B,60-80,显示C,否则显示D
CREATE PROCEDURE test_case(IN score INT)
BEGIN
	CASE 
	WHEN score >= 90 AND score <= 100 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结构
/*
功能:实现多重分支
语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;
...
else 语句n;
end if;

应用在begin end中
*/

# 案例1:创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,返回A,80-90,返回B,60-80,返回C,否则返回D
CREATE FUNCTION test_if(score INT) RETURN CHAR
BEGIN
	IF score >= 90 AND score <= 100 THEN RETURN 'A';
	ELSEIF score >= 80 THEN RETURN 'B';
	ELSEIF score >= 60 THEN RETURN 'C';
	ELSE RETURN 'D';
	END IF;
	
END $

SELECT test_if(86)$


# 二、循环结构
/*
分类:
while、loop、repeat
循环控制:
iterate类似于 continue,继续,结束本次循环,继续下一次
leave类似于 break,跳出,结束当前所在循环
*/


# 1.while
/*
语法:
【标签:】while 循环条件 do
	循环体;
end while【标签】;
*/


# 2.loop
/*
语法:
【标签:】loop
	循环体;
end loop【标签】;

可以用来模拟简单的死循环
*/

# 3.repeat
/*
语法:
【标签:】repeat
	循环体;
until 结束循环的条件
end repeat【标签】;
*/



# 没有添加循环控制语句
# 案例:批量插入,根据次数插入到admin表中多条记录
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i < insertCount DO
		INSERT INTO admin(username, `password`) VALUES(CONCAT('Rose', i), '666');
		SET i = i + 1;
	END WHILE a;
END $

CALL pro_while1(100)$


# 添加leave语句
# 案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i < insertCount DO
		INSERT INTO admin(username, `password`) VALUES(CONCAT('Rose', i), '999');
		IF i >= 20 THEN LEAVE a;
		END IF;
		SET i = i + 1;
	END WHILE a;
END $

CALL test_while1(100)$


# 添加interate语句
# 案例:批量插入,根据次数插入到admin表中多条记录,只插入偶数次
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i < insertCount DO
		IF MOD(i, 2) != 0 THEN ITERATE a;
		END IF;
		INSERT INTO admin(username, `password`) VALUES(CONCAT('Rose', i), '999');
		SET i = i + 1;
	END WHILE a;
END $

CALL test_while1(100)$


/*
已知表stringcontent
其中字段:
id 自增长
content varchar(20)

向该表插入指定个数的,随机字符串
*/
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
	id INT PRIMARY KEY AUTO_INCREMENT,
	content VARCHAR(20)
);

CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
	DECLARE startIndex INT DEFAULT 1;    #代表起始索引
	DECLARE len INT DEFAULT 1;           #代表截取的字符长度
	WHILE i < insertCount DO
		SET startIndex = FLOOR(RAND() * 26 + 1);    #产生一个随机的整数,代表起始索引1-26
		SET len = FLOOR(RAND() * (20 - startIndex + 1) + 1); #产生一个随机的整数,代表截取长度
		INSERT INTO stringcontent(content) VALUES(SUBSTR(str, startIndex, len));
		SET i = i + 1; #循环变量更新
	END WHILE;
END $
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 学习笔记整理 一、Linux 简介 Linux,全称 GNU/Linux,是一种自由和开放源代码的类 Unix 操作系统,它主要是由 Linus Torvalds 在 1991 年创建的。Linux 操作系统具有多用户、多任务、多线程、支持多种文件系统、网络功能强大等优点。 二、Linux 基础命令 1. 基本操作命令 cd:切换目录,例如:cd /usr/local ls:查看目录下文件,例如:ls /usr/local mkdir:创建目录,例如:mkdir test rmdir:删除目录,例如:rmdir test cp:复制文件,例如:cp a.txt b.txt rm:删除文件,例如:rm a.txt mv:移动文件或修改文件名,例如:mv a.txt b.txt 或 mv a.txt /usr/local 2. 文件操作命令 cat:查看文件内容,例如:cat a.txt more:分页显示文件内容,例如:more a.txt less:分页显示文件内容,例如:less a.txt head:显示文件头部内容,例如:head a.txt tail:显示文件尾部内容,例如:tail a.txt grep:查找文件中的关键字,例如:grep "hello" a.txt 3. 权限相关命令 chmod:修改文件权限,例如:chmod 755 a.txt chown:修改文件所属用户和组,例如:chown user:group a.txt su:切换用户,例如:su root sudo:以管理员权限执行命令,例如:sudo apt-get install nginx 4. 网络相关命令 ping:测试网络连通性,例如:ping www.baidu.com ifconfig:查看网络接口信息,例如:ifconfig netstat:查看网络连接状态,例如:netstat -an nslookup:解析域名,例如:nslookup www.baidu.com 5. 系统管理命令 ps:查看进程状态,例如:ps -ef top:实时查看系统资源占用情况,例如:top kill:终止进程,例如:kill 1234 free:查看系统内存使用情况,例如:free -m df:查看磁盘使用情况,例如:df -h 三、常用 Linux 发行版 1. CentOS CentOS(Community Enterprise Operating System)是一款基于 Red Hat Enterprise Linux(RHEL)源代码的 Linux 发行版,它是一个稳定、可靠且高性能的服务器操作系统,广泛用于企业服务器领域。 2. Ubuntu Ubuntu 是一款基于 Debian 的 Linux 发行版,它以易用性、稳定性和安全性著称,适合用于桌面和服务器领域。 3. Debian Debian 是一个由志愿者组成的开源社区开发的 Linux 发行版,以稳定性和安全性著称,适合用于服务器和桌面领域。 4. Fedora Fedora 是由 Red Hat 公司赞助的一个社区项目,它是一个面向桌面用户的 Linux 发行版,以最新的软件包和技术为特点。 四、Linux 常用软件 1. Apache Apache 是世界上最流行的 Web 服务器软件之一,它是一款免费、开源的软件,支持多种操作系统和编程语言,适用于企业和个人网站搭建。 2. MySQL MySQL 是一款流行的关系型数据库管理系统,它是免费、开源的软件,支持多种操作系统和编程语言,适用于企业和个人网站数据存储。 3. Nginx Nginx 是一款高性能的 Web 服务器软件,它是免费、开源的软件,支持多种操作系统和编程语言,适用于企业和个人网站搭建。 4. Git Git 是一款流行的分布式版本控制系统,它是免费、开源的软件,支持多种操作系统和编程语言,适用于团队协作和代码管理。 五、Linux 学习资源推荐 1. Linux 命令大全 2. Linux 教程 3. Linux 学习路径 4. Linux 知识库 5. Linux 命令在线手册 六、总结 本文介绍了 Linux 的基础知识、常用命令、常用发行版、常用软件和学习资源,希望能够帮助读者快速了解 Linux 并入门。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值