MySQL详细教程笔记(基础篇)

一、数据库的好处

 1、实现数据持久化
​ 2、使用完整的管理系统,统一管理,易于查询

二、数据库概念

 1、DB : 数据库(database): 存储数据的“仓库”。保存有组织的数据。
​ 2、DBMS : 数据库管理系统(Database Management System)。数据库是通过DBMS创建和操作的容器。
​ 3SQL: 结构化查询语言(Structure Query Language),专门用来与数据库通讯的语言。

三、数据库特点

 1、一个数据库中可以有多个表,每个表都有一个名字,用来标识自己。表名具有唯一性。
​ 2、表中的字段可以类比为Java中类的属性理解。

四、MySQL特点

 1、MySQL数据库隶属于MySQLAB公司,总部位于瑞典,后被sun收购,sun呗Oracle收购。

五、Windows下关闭和开启服务

 1、停止服务:new stop 服务名
​ 2、开启服务:net start 服务名

六、连接MySQL

 1、连接远程MySQL: mysql -h localhost(主机地址) -P3306(端口号) -u root -p
​ 2、连接本地MySQL: mysql -u root(用户名) -p

七、MySQL常用命令

 1、显示数据库:show databases;2、打开数据库:use 库名;3、查看当前所在库: select database();4、查看表结构:desc 表名;5、查看MySQL版本:登录状态:select version(); dos命令:mysql -V

八、语法规范

 1、不区分大小写,建议关键字大写,表名、列名小写
​ 2、每条命令最好用分号结尾
​ 3、注释:
​ 单行注释:#注释的文字
​ 单行注释:-- 注释的文字
​ 多行注释:/* 注释的文字 */4、几个表中有要重复存储的数据,一个表真实存储数据,其余的表存储此数据的编号:防止数据冗余,节省空间
​ 5、MySQL不区分字符和字符串,统一用单引号

九、DML数据库操作语言

(可以将数据库操作语言统称为DML,也可拆分为DQL & DML)

sql语句执行顺序:
select 查询列表					  第七步
from 表                           第一步
type join2					  第二步
on 连接条件						  第三步	
where 筛选条件					  第四步
group by 分组字段				  第五步
having 分组后的筛选				  第六步
order by 排序的字段				  第八步
limit offset,size                第九步
1、DQL
进阶一(基础查询):
 基础查询:select (选择,过滤,查看)   查询列表  from 表名;
​ 查询列表可以是:表中字段、常亮、表达式、函数
​ 查询的结果是一个虚拟的表  
(1)查询单个字段
select e.last_name from employees e
(2)查询多个字段	
select e.last_name, e.salary, e.email from employees e
(3)查询所有字段
select * from employees
(4)查询常量值
select 100;
select 'job';5)查询表达式
select 100 * 34;
select 100 % 99;6)查询函数
select version();7)别名
​ 便于理解
​ 如果查询的字段有重复可以用别名区分
​ 查询结果和Java实体类属性对应时,别名方便快捷
​ as 可以省略
​ 别名中有特殊字符,别名加双引号
select 100 % 98 as 取余;8)去重  distinct  关键字
​ 在对字段进行去重的时候,要保证distinct在所有字段的最前面
​ 如果distinct关键字后面有多个字段时,则会对多个字段进行组合去重,只有多个字段组合起来的值是相等的才会被去重
select distinct e.department_id from employees e;9+ 号的作用:MySQL中 + 只是运算符
​ select 100 + 90; 两个操作数都是数值型,则做加法运算
​ select '100' + 90; 其中一个为字符型,MySQL会试图把字符型转换为数值型,
​ 如果转换成功,则继续做加法运算,
​ 如果转换失败,则将字符型转换成0继续做加法,
​ 如果其中一个为null,则结果肯定为null
​ 查询员工名和姓连接成一个字段,并显示为 姓名		
select concat(e.last_name, e.first_name) 姓名 from employees e;
-- concat() 拼接中有一个null值的话,整个结果为null,可用ifnull(e1, e2)   e1为要判断的参数,e2为参数为空的话要显示的值 
select concat(e.last_name, ',', e.first_name, ',', ifnull(e.commission_pct, 'null')) from employees e;
进阶二(条件查询):
 语法:select 查询列表 from 表名 where 筛选条件;​ 执行顺序:查看是否存在表---->筛选条件过滤---->查询列表​ 分类:  1、按条件表达式筛选:	条件运算符:>  <  =  !=  <>  >=  <=	建议使用 <>  2、按逻辑表达式筛选:    逻辑运算符:用于连接条件表达式,类比于    Java:	&& || !    MySQL: and or not    不在90-110之间	select * from employees e where not(e.department_id >=90 and e.department_id <= 110)  3、模糊查询:like  	特点:      (1)一般与通配符搭配使用	   通配符:		   	-- % 表示任意多个字符,包括0个字符;		select e.last_name from employees e where e.last_name like '%a%';				-- _下划线表示任意单个字符		-- 第一个字符为W第三个字符为l		select * from employees e where e.last_name like 'M__l%';		-- 查询员工名第二个字符是下划线, \ 进行转义		select * from employees e where e.last_name like '_\_%';		-- 自定义转义符,推荐		select * from employees e where e.last_name like '_$_%' escape '$';	  (2)between and 	    -- between and 为闭区间		select * from employees e where e.department_id between 10 and 20;	  (3)in 		select * from employees e where e.job_id in ('AD_VP', 'IT_PROG')	  (4)is null | is not null	    select e.last_name, e.commission_pct from employees e where e.commission_pct is null;  4、安全等于:<=> 可以判断null也可以判断普通值  5、面试题:    -- 两条sql的结果,如果where条件的两个字段数据有null则结果不一样    select * from employees;    select * from employees e where e.commission_pct like '%%' and e.last_name like '%%';  6、isnull函数:判断字段是否为null,如果是则返回1,否则返回0
进阶三(排序查询):
order by : 默认asc升序,加desc降序。order by 后面的字段支持别名-- 按照表达式排序-- 需求:按照年薪排序select *,e.salary*12*(1 + ifnull(e.commission_pct, 0)) as 年薪 from employees e order by e.salary*12*(1 + ifnull(e.commission_pct, 0)) desc-- 按照函数排序-- 需求:按照姓名的长度显示员工的姓名和工资select length(e.last_name), e.salary from employees e order by length(e.last_name);-- 按多个字段排序—— 需求:查询员工信息,显按照工资升序,在按照编号降序select * from employees e order by e.salary asc, e.employee_id desc;
进阶四(常见函数):
 概念:类似于Java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
​ 好处:隐藏了实现细节,提高代码重用
​ 语法:select 函数名(实参列表) 【from表】
​ 分类:
-- 单行函数
    #字符函数:
    #length()  字节长度
    select length('john');
    -- 查看字符集
    show variables like '%char%'  
    
    #concat()  拼接字符串
    select concat(e.last_name, e.first_name) from employees e;
    
    #upper() & lower()
    select upper('joHn');
    select lower('John');
    
    #substr/substring 截取字符
    -- MySQL中索引都是从1开始
    select substring('李莫愁爱上了陆展元', 7) out_put; #截取指定索引处以及后面的所有字符
    select substring('李莫愁爱上了陆展元', 1, 3) out_put;  #截取指定索引处指定字符长度的字符
    #姓名中首字符大写,其他字符小写,然后用_拼接
	select concat(upper(substring(e.last_name,1, 1)), '_', lower(substring(e.last_name, 2))) 
	from employees e
	
	#instr
	-- 返回第二个参数在第一个参数的起始索引,如果有多个则返回第一个,如果没有则返回0
	-- 即返回子串第一次出现的索引,如果找不到则返回0
	select instr('爱上了陆展元', '李莫愁') as out_put;
	
	#trim 去除前后空格;去除前后指定字符
	select trim('    张无忌    ') as out_put;
	select trim('a' from 'aaaaa张无忌aaaaaaa') as out_put;
	
	#lpad 指定字符左填充指定长度
	select lpad('漩涡鸣人', 10, '*') as out_put;
	#rpad 指定字符右填充指定长度
	select rpad('漩涡鸣人', 10, '*') as out_put;
	#指定长度比原始字符小,则截取,rpad从右截取,lpad从左截取
	select rpad('漩涡鸣人', 2, '*') as out_put;
	
	# replace 全部替换
	SELECT REPLACE('漩涡鸣人爱上了小樱', '小樱', '雏田') AS out_put;
	
    #数学函数:
    
	# round 四舍五入,支持负数,默认不保留小数位
	SELECT round(5.55) AS out_put;
	#保留指定小数位
	SELECT round(3.1231, 3) AS out_put; 
	
	# ceil 向上取整,返回 >= 该参数的最小整数
	SELECT CEIL(1.00) AS out_put;

	# floor 向下取整,返回 <= 该参数的最大整数
	SELECT floor(-9.99)
	
	# truncate 截断(保留几位小数的意思)
	SELECT truncate(111.123, 1) AS out_put;
	
	# mod 取余,被除数是正数则余数为正数
    SELECT MOD(10, -3) AS out_put;
    # 等价
    SELECT (-10) % 3;

    #日期函数:
    
    # now 返回当前系统日期加时间
	SELECT now();

	# curdate 返回当前系统日期
	SELECT curdate();

	# curtime 返回当前系统的时间
	SELECT curtime();

    # 获取指定的部分,年、月、日、时、分、秒
	SELECT 
		YEAR(now()) AS m, MONTH(now()) AS M, monthname(now()) MNAME,
		DAY(now()) AS d, HOUR(now()) AS h, MINUTE(now()) AS m, SECOND(now()) AS s;
		
	# 将日期格式的字符串转换成指定格式的日期
	SELECT str_to_date('2021-08-05', '%Y-%m-%d') AS my_date;

	# 将日期转换为字符串
	SELECT date_format(now(), '%Y-%m-%d');	
    
    -- %Y 四位的年份  %y 2位的年份  %m 月份(01,02,...12)  %c 月份(1,2,...12)  %d 日(01,02...)
    -- %H 小时(24)  %h 小时(12) %i 分钟(00,01...59)  %s 秒(00,01...59)
    
    # 练习:查询有奖金的员工名和入职日期(xx月/xx日  xx年)
	SELECT e.last_name, date_format(e.hiredate, '%m月-%d日  %Y年') FROM employees e 
	WHERE e.commission_pct IS NOT NULL

    #其他函数:
    
    # 查询系统版本号
	SELECT version();

    # 查询当前数据库
    SELECT DATABASE();

    # 查询当前用户
    SELECT USER();

    #流程控制函数:	
    
    # if 类似Java的三元运算符
	SELECT IF(e.commission_pct IS NULL, '无奖金', '有奖金') FROM employees e;
	
	# case 1、 类似Java switch case
    /*
     * case 要判断的字段或表达式
     * when 常量1 then 要显示的值1或语句1;
     * when 常量2 then 要显示的值2或语句2;
     * ...
     * else 要显示的值n 或语句n;
     * end
     * 
     * 在select后当表达式使用,case then 不能接语句,在存储过程中可以
     */
    /*
     * 案例:查询员工的姓名和公司
     * 部门号=30,显示的工资为1.1倍
     * 部门号=40,显示的工资为1.2倍
     * 部门号=50,显示的工资为1.3倍
     * 其他部门显示原工资
     */
    SELECT 
        e.last_name,
        e.salary AS old_salary,
        CASE e.department_id 
            WHEN 30 THEN e.salary * 1.1 
            WHEN 40 THEN e.salary * 1.2 
            WHEN 50 THEN e.salary * 1.3 
            ELSE salary
        END AS new_salary
    FROM employees e;
    
    /*
     * case 2、类似Java的多重if
     * case
     * when 条件1 then 要显示的值1或语句1
     * when 条件2 then 要显示的值2或语句2
     * 。。。
     * else 要显示的值n或语句n
     * end
     */
    /*
     * 案例根据员工的工资分等级
     * salary>20000, A
     * salary>15000, B
     * salary>10000, C
     * else D
     */
    SELECT 
        e.last_name,
        e.salary,
        CASE
            WHEN e.salary > 20000 THEN 'A'
            WHEN e.salary > 15000 THEN 'B'
            WHEN e.salary > 10000 THEN 'C'
            ELSE 'D'
        END AS salary_level
    FROM employees e;

-- 分组函数,做统计使用,又称为统计函数或聚合函数或组函数
	
	# sum 求和 
    SELECT sum(e.salary) AS sum_salary FROM employees e;

    # avg 平均值
    SELECT round(avg(e.salary), 2) AS avg_salary FROM employees e;

    # max 最大值
    SELECT max(e.salary) FROM employees e;

    # min 最小值
    SELECT min(e.salary) FROM employees e;

    # count 计算个数
    SELECT count(e.employee_id) FROM employees e;
    
    /*
     * 1、  sum、avg 用于数值型处理,max、min、count可以处理任何类型
     * 2、  以上分组函数都忽略null
     */
    # 以上分组函数都支持和distinct搭配使用
    SELECT sum(distinct e.salary), sum(e.salary) FROM employees e;
    
    /*
     * count 函数的具体用法
     * count(字段) 当前字段为null,则不计数
     * count(*)   当前行有一个字段不为null就计数
     * count(1)  相当于在表加一列值全是1的列,然后统计1的行数
     * 效率:
     * 	MYISAM 存储引擎下,count(*) 效率高
     * 	INNODB 存储引擎下,count(*) 和 count(1) 效率差不多,都比count(字段)高一些。
     */
     
     /*
      * 和分组函数一同查询的字段有限制,
      * 要求是 group by 后面的字段
      */

    # 查询员工表中最大入职时间和最小入职时间相差天数, datediff(e1, e2) e1 e2 相差的天数
    SELECT datediff (max(e.hiredate), min(e.hiredate)) FROM employees e;
进阶五(分组查询):
/** * group by 语法: * select 分组函数,列(要求出现在group by 的后面) * from 表 * 【where 筛选条件】 * group by 分组列表 * 【order by 子句】 *  * 注意:查询列表比较特殊,要求是分组函数和group by 后面出现的字段 */# 查询每个部门的平均工资SELECT e.department_id, avg(e.salary) FROM employees e WHERE e.department_id IS NOT NULL GROUP BY e.department_id ORDER BY e.department_id; #案例一:每个工种的最高工资SELECT e.job_id, max(e.salary) FROM employees e GROUP BY e.job_id;# 案例二:每个位置上的部门个数SELECT location_id,count(department_id) FROM departments d GROUP BY d.location_id;# 案例三:查询邮箱中包含a字符的,每个部门的平均工资SELECT e.department_id, avg(e.salary) FROM employees e WHERE e.email LIKE '%a%' AND department_id IS NOT NULL GROUP BY e.department_id;# 案例四:查询每个领导手下有奖金的员工的最高工资SELECT e.manager_id, max(e.salary) FROM employees e WHERE e.commission_pct IS NOT NULL GROUP BY e.manager_id;# 案例五:查询哪个部门的员工个数>2SELECT e2.department_id,count(*) FROM employees e2 GROUP BY e2.department_id HAVING count(*) > 2;# 案例六:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资SELECT e.job_id, max(e.salary) AS max_salary FROM employees eWHERE e.commission_pct IS NOT NULL GROUP BY e.job_id HAVING max_salary > 12000;# 案例七:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及最低工资SELECT e.manager_id, min(e.salary) AS min_salary FROM employees e WHERE e.manager_id > 102 GROUP BY e.manager_id HAVING min_salary > 5000;/** * where 属于分组前的筛选,having 属于分组后的筛选 * 如果是根据原始表字段筛选就放在where后面,否则放在having后面 * 分组前的数据源是原始表,分组后的数据源是分组后的结果集 * 分组函数做条件肯定放在having 后面 * 优先选择分组前筛选 */  -- 按表达式或者函数分组 -- MYSQL group by 与 having 后面支持别名,where后不支持, Oracle 都不支持 # 案例一 :按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数大于5的有哪些SELECT 	LENGTH(e.last_name) AS name_length,  	count(*) AS em_numFROM employees eGROUP BY name_length HAVING em_num > 5;-- 按多个字段分组#  案例一:查询每个部门每个工种的员工的平均工资,并且由高到低展示SELECT 	e.department_id, 	e.job_id,	avg(e.salary)FROM employees eGROUP BY e.department_id, e.job_idORDER BY avg(e.salary) desc;
进阶六(连接查询):
 分类:
	按年代分类:
		sql92标准:MySQL中仅仅支持内连接
		sql99标准【推荐】:MySQL中支持内连接+外连接(左外+右外)+交叉连接
	按功能分类:
		内连接:
			等值连接
			非等值连接
			自连接
        外链接:
        	左外链接
        	右外链接
        	全外连接
        交叉连接:
/**
 *  笛卡尔乘积现象:表1有m行,表2有n行,结果=m*n行
 * 造成现象原因:没有有效的连接条件
 */
#  结果是笛卡尔积
SELECT be.name, bo.`boyName` FROM beauty be, boys bo;
# 增加连接条件
SELECT be.name, bo.`boyName` FROM beauty be, boys bo WHERE be.boyfriend_id = bo.id;

-- -------------------------------------------- sql92标准
-- 1、等值连接   (注意:如果为表起了别名,那么就不能用全表名限定字段)

# 案例一:查询男女朋友能对应上的数据
SELECT be.name, bo.`boyName` FROM beauty be, boys bo WHERE be.boyfriend_id = bo.id;
# 案例二:员工名和对应的部门名
SELECT e.last_name, d.department_name FROM employees e, departments d
WHERE e.department_id = d.department_id;

# 案例三:查询城市中第二个字符为o的部门名称和城市(等值连接查询时可以添加其余的筛选条件)
SELECT d.department_name, l.city FROM departments d, locations l
WHERE d.location_id = l.location_id AND d.department_name LIKE '_o%';

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

# 案例六(等值连接可加排序):查询每个工种的工种名和员工的个数,并按照员工的个数降序
SELECT j.job_title, count(*) FROM jobs j, employees e
WHERE j.job_id = e.job_id
GROUP BY j.job_id
ORDER BY count(*) DESC;

# 案例七(等值连接三表连接):查询员工名,部门名,所在城市名
SELECT e.last_name, d.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;

/**
 	1、多表等值连接为多表的交集部分
 	2、n表连接,最少需要n-1个连接条件
 	3、多表的顺序没有要求
 	4、一般要为表起别名
 	5、可以搭配大部分子句,例如排序、分组、筛选
 */

-- 2、非等值连接

# 案例一:查询员工的工资和工资级别
SELECT e.salary, j.grade_level FROM employees e, job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest
ORDER BY e.salary desc;

-- 3、自连接
# 案例一:查询员工名称和上级名称
SELECT 
	e.last_name, e.employee_id,
	m.last_name, m.employee_id
FROM employees e, employees m
WHERE e.employee_id = m.manager_id;

-- ------------------------------------ sql99标准   sql99标准【推荐】:MySQL中支持内连接+外连接(左外+右外)+交叉连接
内连接:inner  
外链接:
	左外:left
	右外:right
	全外:full    
交叉连接:cross

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

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

# 案例三:查询部门个数>3的城市名和部门个数,(添加分组+筛选)
SELECT l.city, count(*) FROM locations l 
INNER JOIN departments d ON d.location_id = l.location_id
GROUP BY l.city HAVING count(*) > 3;

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

# 案例五:查询员工名、部门名、工种名,并按部门名降序 (多表连接,要保证前面的表和后面的表有关联关系,如本题不能from直接加job表)
SELECT e.last_name, d.department_name, j.job_title FROM employees e 
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN jobs j ON j.job_id = e.job_id
ORDER BY d.department_name DESC;

-- sql99语法-内连接-非等值连接
# 案例一:查询员工名,工资以及工资级别
SELECT e.last_name, e.salary, j.grade_level FROM employees e 
INNER JOIN job_grades j ON e.salary BETWEEN j.lowest_sal AND j.highest
ORDER BY e.salary DESC;

-- sql99语法-外连接
应用场景:用于查询一个表中有,一个表中没有的记录
特点:1、外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null
	 外连接查询结果 = 内连接结果 + 主表中有而从表没有的记录
	 2、左外连接,left join 左边的是主表
	    右外连接,right join 右边的是主表
	 3、左外和右外两个表交换位置同样可以达到相同的效果
	 4、全外连接 = 内连接的结果 +1中有但是表2没有的 +2中有但是表1中没有的
	 
# 案例一:查询男朋友不在男神表的女神名
SELECT be.name FROM beauty be LEFT JOIN boys bo ON be.boyfriend_id = bo.id 
WHERE bo.`id` IS NULL;

#案例二: 查询哪个部门没有员工
SELECT * FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;

# 全外连接(不支持此语发)
SELECT * FROM beauty be FULL JOIN boys bo ON be.boyfriend_id = bo.id;

# 交叉连接,结果就是个笛卡尔乘积
SELECT be.*, bo.* FROM beauty be CROSS JOIN boys bo;

-- 练习题
# 习题一:查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有则用null填充
SELECT be.name, bo.* FROM beauty be LEFT JOIN boys bo ON bo.id = be.boyfriend_id
WHERE be.id > 3 ORDER BY bo.id;

#习题二:查询哪个城市没有部门
SELECT l.city FROM locations l LEFT JOIN departments d ON l.location_id = d.location_id
WHERE d.department_id IS NULL;

#习题三:查询部门名为SAL或IT的员工信息
SELECT d.department_name, e.* FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id
WHERE d.department_name IN ('SAL', 'IT');
进阶七(子查询):
-- 1、含义:出现在其他语句中的select语句,称为子查询或者内查询,外部的查询语句称为主查询或者外查询
-- 2、分类:
--    按照子查询出现的位置:
	  select 后面,仅仅支持标量子查询
	  from 后面,支持表子查询 
	  wherehaving 后面,支持标量子查询、列子查询、行子查询(用的少)
	  exists后面(称为相关子查询),表子查询
--    按照结果集的行列数不同:
--       标量 子查询:结果集只有一行一列
--       列 子查询:结果只有一列多行
--       行 子查询:结果只有一行多列
--       表 子查询:结果集一般为多行多列wherehaving 后面加子查询
/*
特点:
(1) 子查询放在小括号内
(2) 子查询一般放在条件的右侧
(3) 标量子查询,一般搭配单行操作符:>  <  = <> ...
(4) 列子查询,一般搭配多行操作符使用:in any/some all ...
(5) 子查询的执行优先于主查询的执行,因为主查询的条件用到了子查询的结果
*/
#1、标量子查询(单行子查询)

# 案例一:谁的工资比 Abel 高?
SELECT e.last_name, e.salary FROM employees e
WHERE e.salary > (
	SELECT e2.salary FROM employees e2 WHERE e2.last_name = 'Abel'
);

#案例二:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
SELECT 
	e1.last_name,  
	e1.job_id,
	e1.salary
FROM employees e1
WHERE e1.job_id = (SELECT e2.job_id FROM employees e2 WHERE e2.employee_id = 141)
AND e1.salary > (SELECT e3.employee_id FROM employees e3 WHERE e3.employee_id = 143);

# 案例三:返回公司工资最少的员工的last_name, job_id, salary
SELECT e1.last_name, e1.job_id, e1.salary FROM employees e1
WHERE e1.salary = (SELECT min(e.salary) FROM employees e);

# 案例四:查询最低工资>50号部门最低工资 的   部门id和其最低工资
SELECT e1.department_id, min(e1.salary) FROM employees e1 GROUP BY e1.department_id
HAVING min(e1.salary) > (
	SELECT min(e2.salary) FROM employees e2 WHERE e2.department_id = 50 
);

#2、列子查询(多行子查询)
/*
	搭配多行比较操作符:
	in/not in  
	any/some   
    all
*/
# 案例一:返回location_id是1400或1700的部门中的所有员工姓名
SELECT e.last_name FROM employees e
WHERE e.department_id IN (
	SELECT DISTINCT d.department_id FROM departments d WHERE d.location_id IN (1400, 1700)
);

#  案列二:返回其它工种中比job_id为'IT_PROG'部门任一工资低的员工的员工号、姓名 、job_id以及salary
-- 1、job_id为'IT_PROG'部门任一工资
SELECT DISTINCT e.salary FROM employees e WHERE e.job_id = 'IT_PROG';
-- 2、查询员工号、姓名 、job_id以及salary,salary
SELECT e.employee_id, e.last_name, e.job_id, e.salary FROM employees e
WHERE e.salary < ANY(
	SELECT DISTINCT e.salary FROM employees e WHERE e.job_id = 'IT_PROG'
) AND e.job_id <> 'IT_PROG';

#3、行子查询(一行多列或者多行多列)

# 案例一:查询员工编号最小并且工资最高的员工信息
-- 常规写法写法
SELECT * FROM employees e
WHERE e.employee_id = (
	SELECT min(e2.employee_id) FROM employees e2
) AND e.salary = (
	SELECT max(e3.salary) FROM employees e3
);
-- 行子查询,两个或者多个条件都是相同的才行, 多个字段当成一个虚拟的字段来用
SELECT * FROM employees e 
WHERE (e.employee_id, e.salary) = (
	SELECT min(e2.employee_id), max(e2.salary) FROM employees e2
);select 后面,仅仅支持标量子查询
# 案例一: 查询部门信息和每个部门的员工个数
SELECT 
	d.*, 
	(SELECT count(*) FROM employees e WHERE e.department_id = d.department_id) 员工个数
FROM departments d;from 后面,支持表子查询 
# 案例一: 查询每个部门平均工资的工资等级
SELECT 
	avg_dep.department_id,
	avg_dep.avg_salary,
	j.grade_level
FROM (
	SELECT e.department_id, avg(e.salary) avg_salary FROM employees e
	GROUP BY e.department_id HAVING e.department_id IS NOT NULL
) avg_dep INNER JOIN job_grades j ON avg_dep.avg_salary BETWEEN j.lowest_sal AND j.highest;exists后面(称为相关子查询),表子查询
​ exists(完整的查询语句) 结果:1(表示括号中的语句有记录)0(表示括号中的语句无记录)
# 案例一:查询有员工的部门名
SELECT d.department_name FROM departments d
WHERE EXISTS (
	SELECT e.department_id FROM employees e WHERE d.department_id = e.department_id
);
-- 用 IN 的写法
SELECT d.department_name FROM departments d
WHERE d.department_id IN (
	SELECT e.department_id FROM employees e
);
进阶八(分页查询):
 语法: limit offset, size;offset 要显示条目的起始索引(起始索引从0开始),size要显示的条数​ 特点:    1limit无论是在语法还是执行顺序都在最后	2、分页公式:limit (page -1)*size, size# 案例一:查询前五条员工信息SELECT * FROM employees LIMIT 0, 5;# 案例一:查询前五条员工信息(offset从第一条开始的话可以省略)SELECT * FROM employees LIMIT 5;# 案例二:查询第11条到第25条员工信息SELECT * FROM employees LIMIT 10, 15;# 案例三:查询有奖金的员工信息,并且是工资较高的前十名SELECT * FROM employees eWHERE e.commission_pct IS NOT NULL ORDER BY e.salary LIMIT 0, 10;
经典子查询案例:
# 案例一:查询工资最低的员工信息:last_name,salary-- 查询最低工资SELECT min(e.salary) FROM employees e;SELECT e.last_name, e.salary FROM employees eWHERE e.salary = (	SELECT min(e1.salary) FROM employees e1);# 案例二:查询平均工资最低的部门信息-- 1.查询每个部门的平均工资SELECT avg(e.salary) avg_salary, e.department_id FROM employees e WHERE e.department_id IS NOT NULLGROUP BY e.department_id;-- 2.查询每个部门的最低平均工资SELECT min(a.avg_salary) FROM (	SELECT avg(e.salary) avg_salary, e.department_id FROM employees e 	WHERE e.department_id IS NOT NULL	GROUP BY e.department_id) a;-- 3.平均工资最低的部门idSELECT e.department_id FROM employees e WHERE e.department_id IS NOT NULLGROUP BY e.department_id HAVING avg(e.salary) = (	SELECT min(a.avg_salary) FROM (		SELECT avg(e.salary) avg_salary, e.department_id FROM employees e 		WHERE e.department_id IS NOT NULL		GROUP BY e.department_id	) a); -- 4.查询平均工资最低的部门信息SELECT d.* FROM departments dWHERE d.department_id = (	SELECT e.department_id FROM employees e 	WHERE e.department_id IS NOT NULL	GROUP BY e.department_id HAVING avg(e.salary) = (		SELECT min(a.avg_salary) FROM (			SELECT avg(e.salary) avg_salary, e.department_id FROM employees e 			WHERE e.department_id IS NOT NULL			GROUP BY e.department_id		) a	) );-- 简单写法-- 1.查询平均工资最低的部门idSELECT e.department_id FROM employees eGROUP BY e.department_idORDER BY avg(e.salary) ASCLIMIT 1;-- 2.查询平均工资最低的部门信息SELECT d.* FROM departments dWHERE d.department_id = (	SELECT e.department_id FROM employees e	GROUP BY e.department_id	ORDER BY avg(e.salary) ASC	LIMIT 1);# 案例三:查询平均工资最低的部门信息和该部门的平均工资-- 写法一:from 后面,表子查询 SELECT d.*, a.avg_salary FROM departments d INNER JOIN (	SELECT avg(e.salary) avg_salary,e.department_id FROM employees e	GROUP BY e.department_id	ORDER BY avg(e.salary) ASC	LIMIT 1) a ON d.department_id = a.department_idWHERE d.department_id = (	SELECT e.department_id FROM employees e	GROUP BY e.department_id	ORDER BY avg(e.salary) ASC	LIMIT 1);-- 写法二:select 后面,仅仅支持标量子查询SELECT 	d.*,	(SELECT avg(e.salary) avg_salary FROM employees e	GROUP BY e.department_id	ORDER BY avg(e.salary) ASC	LIMIT 1)	FROM departments d WHERE d.department_id = (	SELECT e.department_id FROM employees e	GROUP BY e.department_id	ORDER BY avg(e.salary) ASC	LIMIT 1);# 案例四:查询平均工资最高的job信息-- 1.查询平均工资最高的job_idSELECT e.job_id FROM employees eGROUP BY e.job_idORDER BY avg(e.salary) DESCLIMIT 1;-- 2.查询平均工资最高的job信息SELECT j.* FROM jobs j WHERE j.job_id = (	SELECT e.job_id FROM employees e	GROUP BY e.job_id	ORDER BY avg(e.salary) DESC	LIMIT 1);# 案例五:查询平均工资高于公司平均工资的部门有哪些?-- 1.查询公司的平均工资SELECT avg(e.salary) FROM employees e;-- 2.查询平均工资高于公司平均工资的部门idSELECT department_id FROM employees e WHERE department_id IS NOT NULL GROUP BY e.department_id HAVING avg(e.salary) > (	SELECT avg(salary) FROM employees);-- 3.查询平均工资高于公司平均工资的部门有哪些SELECT d.* FROM departments dWHERE d.department_id IN (	SELECT department_id FROM employees e 	WHERE department_id IS NOT NULL 	GROUP BY e.department_id HAVING avg(e.salary) > (		SELECT avg(salary) FROM employees	));# 案例六:查询出公司中所有manager的详细信息-- 1.查询所有manager的员工编号SELECT DISTINCT e.manager_id FROM employees e WHERE e.manager_id IS NOT NULL;-- 2.查询出公司中所有manager的详细信息SELECT e.* FROM employees e WHERE e.employee_id IN (	SELECT DISTINCT e.manager_id FROM employees e WHERE e.manager_id IS NOT NULL);# 案例七:各个部门中,最高工资中最低的那个部门的  最低工资是多少-- 1.各个部门中的最高工资最低的部门idSELECT e.department_id FROM employees e GROUP BY e.department_idORDER BY max(e.salary) ASCLIMIT 1;-- 2.各个部门中,最高工资中最低的那个部门的  最低工资是多少SELECT min(e.salary) FROM employees eWHERE e.department_id = (	SELECT e.department_id FROM employees e 	GROUP BY e.department_id	ORDER BY max(e.salary) ASC	LIMIT 1);# 案例八:查询平均工资最高的部门的 manager的详细信息:last_name, department_id, email, salary-- 查询平均工资最高的部门idSELECT e.department_id FROM employees eWHERE e.department_id IS NOT NULL GROUP BY e.department_idORDER BY avg(e.salary) DESCLIMIT 1;-- 查询符合条件的manager idSELECT DISTINCT e.manager_id FROM employees e WHERE e.manager_id IS NOT NULL AND e.department_id = (	SELECT e.department_id FROM employees e	WHERE e.department_id IS NOT NULL 	GROUP BY e.department_id	ORDER BY avg(e.salary) DESC	LIMIT 1); -- 查询平均工资最高的部门的 manager的详细信息SELECT e.last_name, e.department_id, e.email, e.salary, e.manager_id FROM employees eWHERE e.employee_id IN (	SELECT DISTINCT e.manager_id FROM employees e WHERE e.manager_id IS NOT NULL AND e.department_id = (		SELECT e.department_id FROM employees e		WHERE e.department_id IS NOT NULL 		GROUP BY e.department_id		ORDER BY avg(e.salary) DESC		LIMIT 1	)	);
进阶九(联合查询):
 union(联合/合并):将多条查询语句的结果合并成一个结果​ 语法:	查询语句一	union	查询语句二​ 应用场景:	要查询的结果来自多个表,且多个表没有直接的连接关系,但是查询的信息一致时​ 特点:	1.多条查询语句的查询列数是一致的	2.多条查询语句查询的每一列的类型和顺序最好一致	3.union关键字默认是去除重复的,union all 可以去重
2、DML
 数据库操作语言:	插入:insert	修改:update	删除:delete
插入
 语法一:	INSERT INTO TABLE (列名) VALUES ();​ 特点:	1.插入的值的类型要与列的类型一致或兼容	2.可以为空的字段处理:(1)不写列名和值(2)写列名值写null	3.列的顺序可以颠倒,值对应即可	4.省略列名,即默认所有列,并且列的顺序和表的顺序一致	# 案例一:向beauty表添加一条数据INSERT INTO beauty (id, name, sex, borndate, phone, photo, boyfriend_id)VALUES (13, '瑞文', '女', '1995-06-06', '17845614811', NULL, 2);​ 语法二:	insert into 表名 set 列名=值, 列名=值,...​ 语法一与语法二比较:	1.语法一支持插入多行,语法二不支持	INSERT INTO beauty (id, name, sex, borndate, phone, photo, boyfriend_id) VALUES     (20, '瑞文2', '女', '1995-06-06', '17845614811', NULL, 2),    (21, '瑞文3', '女', '1995-06-06', '17845614811', NULL, 2),    (22, '瑞文4', '女', '1995-06-06', '17845614811', NULL, 2);    2.语法一支持子查询,语法二不支持    INSERT INTO beauty (id, name, phone)	SELECT b.id, b.`boyName`, '4123421' FROM boys b WHERE b.id = 23;
修改
一、修改单表记录:	语法:	update 表名 set 列名=值,列名=... where 筛选条件;# 案例一:修改beauty表中几个瑞文的电话为13896452366UPDATE beauty SET phone = '13896452366' WHERE name LIKE '瑞文%';二、修改多表记录:	语法:		sql92:		update 表1 别名, 表2 别名		set 列=值,...		where 连接条件 and 筛选条件;		sql99:		update 表1 别名 		inner|left|right join 表2 别名        on 连接条件        set 列=值,...        where 筛选条件# 案例一:修改张无忌的女朋友的手机号为114UPDATE beauty be INNER JOIN boys bo ON be.boyfriend_id = bo.idSET be.phone = '114' WHERE bo.`boyName` = '张无忌';
删除
方式一:delete	单表删除:		语法:			delete from 表名 where 筛选条件;# 案例一:删除手机号是4123421的DELETE FROM beauty WHERE phone = '4123421';	多表删除:		语法:		sql92/sql99都支持# 案例一:删除张无忌的女朋友信息delete b from beauty b inner join boys bo on b.boyfriend_id = bo.idwhere bo.boyName = '张无忌';方式二:truncate	语法:truncate table 表名;delete pk truncate:	1.delete可以加where条件,truncate不行	2.truncate效率高一点	3.删除表中的自增长列时,delete删除后,自增长的值从删除后开始,而truncate删除后,自增长列的值从1开始	4.delete有返回值,truncate没有返回值	5.delete删除可以回滚,truncate删除不可以回滚

十、DDL数据库定义语言

库的管理
一、库的管理:创建(create)、修改(alter)、删除(drop)	​ 创建(create)        语法:create database 库名;        # 案例一:创建books库        CREATE DATABASE books;        # 案例二:有则不创建,没有则创建        CREATE DATABASE IF NOT EXISTS books;    ​ 修改(alter)    	1.修改库名操作不安全,已经废弃(可以直接修改数据库文件的名字,也不建议修改)	    RENAME DATABASE books TO 新库名;		2.更改库的字符集		ALTER DATABASE books CHARACTER SET gbk;	​ 删除(drop)		DROP DATABASE IF EXISTS books;
表的管理
二、表的管理:创建(create)、修改(alter)、删除(drop)
	​ 创建(create)
        语法:create table 表名(
        	列名 列的类型 【(长度) 约束】,
            列名 列的类型 【(长度) 约束】,
            ...
            列名 列的类型 【(长度) 约束】
        );
        # 案例一:创建book、author表
        CREATE TABLE book (
            id int,
            bName varchar(20),
            price double,
            authorId int,
            publishDate datetime
        );
        CREATE TABLE IF NOT EXISTS author (
            id int,
            au_name varchar(20),
            nation varchar(10)
        );
    ​ 修改(alter)
    	1.修改列名(COLUMN 可以省略)
    	ALTER TABLE book CHANGE COLUMN publishDate pubDate datetime;
    	2.修改列的类型或约束
    	ALTER TABLE book MODIFY COLUMN pubdate timestamp;
    	3.添加新列
    	ALTER TABLE author ADD COLUMN annual double;
    	4.删除列
    	ALTER TABLE author DROP COLUMN annual;
    	5.修改表名
    	ALTER TABLE author RENAME TO book_author;
	​ 删除(drop)
		DROP TABLE IF EXISTS book_author;
表的复制
1.仅复制表的结构
CREATE TABLE copy_author LIKE author;
2.复制表的结果和数据
CREATE TABLE copy_author2 SELECT * FROM author;
3.复制部分字段
CREATE TABLE copy_author3 
SELECT id, au_name FROM author 
WHERE 1 = 2;
4.复制部分字段以及数据
CREATE TABLE copy_author4 
SELECT id, au_name FROM author;

十一、数据类型介绍

常见的数据类型
数值型:
	整型:
	小数:
		定点数:
		浮点数:
字符型:
	较短的文本:charvarchar
	较长的文本:textblob(较长的二进制数据)
日期型:
整型
分类:
	tinyintsmallintmediumintint/integerbigint
字节:  1           2           3            4            8

特点:
	1.如何设置无符号和有符号
    	CREATE TABLE tab (
        	t1 int(7) zerofill, # 默认为有符号
            t2 int UNSIGNED # 无符号
        );
    2.存储的值超过当前类型的数值范围,会报 out of range 异常,并且插入临界值(MySQL8似乎就不插入值了)
    3.如果不设置长度,会有默认长度
    4.int7)这里的7指的是显示最大宽大,如果不够会用0在左边填充,但必须搭配 zerofill才能生效,使用 ZEROFILL 强制为无符号。
小数
分类:
	浮点型:
		float(M, D)  double(M, D)
	字节:   4            8
	定点型:
		decimal(M, D)  简写:dec(M, D)    
	字节:M + 2
特点:
	1.M和D
		M:整数部位+小数部位长度
		D:小数部位长度
		如果超过范围就插入临界值
		M和D都可以省略,如果是decimal,则M默认为10,D默认为0
	2.定点型的精度较高,如果要求插入数值的精度较高如货币等运算则考虑使用
原则:
	所选择的类型越简单越好,能保存数值的类型越小越好
字符型
较短文本:
	char(m) : 
		1.m代表能保存的最大字符数,一个字母一个汉字都是一个字符
		2.固定长度字符,例如char(10)保存中国两个字也是开辟10个字符的空间,相对于varchar耗费空间
		3.相比于varchar效率高
	varchar(m) : 
		1.m同上
		2.可变长度字符串,例如varchar(10)只会开辟两个字符的空间,相对char节省空间
		3.相比于char效率低
较长文本:
	text
	blob(比较大的二进制)
binaryvarbinary 类型:
	类似于vhar 和 varchar ,不同的是它们保存二进制字符(一般为较短的,较长的用blobENUM类型:
	create table tab (
    	c1 ENUM('a', 'b', 'c')
    );
    此字段只能保存a, b或c(大写自动变小写),其他的值无法插入。
SET类型:
	create table tab_set (
    	s1 SET('a', 'b', 'c','d','e')
    );
    insert into tab_set values ('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
    
datetimetimestamp 的区别:
	1.取值范围不同
	2.timestamp和实际时区有关,更能反映实际的日期,而datetime则只能反映出插入时的当地时区
	3.timestamp 的属性受MySQL版本和SQLMode的影响很大
常见约束
含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确性和可靠性。
分类:
	not null :非空,用于保证该字段的值不能为空
	default : 默认,表示该字段有默认值
	primary key : 主键,表示该字段的值具有唯一性,并且非空
	unique : 唯一,用于保证该字段的值具有唯一性,可以为空
	check : 检查约束【MySQL中不支持】,例如约束年龄只能是18-60岁
	poreign key : 外键,用于限制两个表的关系,保证该字段的值必须来自于主表的关联列的值,在从表添加外键约束,用于引用主表中某列的值
添加约束的时机:
	1.创建表时
	2.修改表时,一定是未插入数据之前
约束的添加分类:
	列级约束:
		六大约束语法上都支持,但是外键约束没有效果
	表级约束:
		除了非空、默认,其他都支持
语法:
	*************同一个列可以添加多个约束*************
	create table 表名 (
    	字段名  字段类型  列级约束,
        字段名  字段类型,
        ....
        表级约束
    );
    
1、创建表时:
# 案例一:添加列级约束
CREATE TABLE major (
	id int PRIMARY KEY,
	majorName varchar(20)
);
# 主键、外键、唯一都会自动生成索引
CREATE TABLE stuinfo (
	id int PRIMARY KEY, #主键
	stuName varchar(20) NOT NULL, #非空
	gender char(1) CHECK(gender='男' OR gender='女'), #检查(性别不用sex用gender,check约束在MySQL不生效) 
	seat int UNIQUE, #唯一,空为空
	age int DEFAULT 18, #默认约束
	majorId int REFERENCES major(id) #外键 (MySQL不支持列级外键约束) 
);
#查看表结构
DESC stuinfo;
# 查看表的所有索引
SHOW INDEX FROM stuinfo;

# 案例二:添加表级约束
# 删除stuinfo表
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), #主键
	CONSTRAINT uq UNIQUE(seat), #唯一键
	CONSTRAINT ck CHECK(gender='男' OR gender='女'), #检查
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorId) REFERENCES major(id) #外键
);
语法:
	【constraint 约束名】 约束类型(字段名)
通用写法(到底用列级约束还是表级约束):
	create table if not exists stuinfo (
    	id int primary key,
        stuname varchar(20) not null,
        gender char(1),
        age int default 18,
        seat int unique,
        majorid int,
        constraint fk_stuinfo_major foreign key(majorid) references major(id)
    );
    
主键和唯一的对比(经典面试题):
			保证唯一性	是否允许为空	  一个表中可以有多少个    是否允许组合(不推荐使用)
	主键		   是	     否			   至多有一个			是(联合主键)
	唯一         是         是			   可以有多个  			是
	
	# 唯一性,只能插入一个null
	INSERT INTO major VALUES(1, 'Java');
    INSERT INTO major VALUES(2, 'H5');
    SELECT * FROM major;
    INSERT INTO stuinfo VALUES (1, 'John', '男', NULL, 19, 1);
    INSERT INTO stuinfo VALUES (1, 'Tom', '男', NULL, 20, 2);
    SELECT * FROM stuinfo;
外键的特点:
	1.要求在从表设置外键关系
	2.从表的外键列的类型和主表的关联列的类型一致或者兼容,名称无要求
	3.主表的关联列必须是一个key(一般是主键或唯一)
	4.插入时,先插入主表,再插入从表;删除时,先删除从表,再删除主表

2、修改表时添加约束:
	#1. 添加非空约束
    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);
3、修改表时删除约束
    #1.删除非空约束
    ALTER TABLE stuinfo MODIFY COLUMN stuname varchar(20) NULL;
    #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;
标识列
标识列又称为自增长列,即可以不用手动的插入值,系统提供默认的序列值
#一、创建表时设置
CREATE TABLE tab_identity(
	id int PRIMARY KEY AUTO_INCREMENT,
	name varchar(20)
);
# 查看步长、初始值
SHOW variables LIKE '%auto_increment%';
#设置步长
SET auto_increment_increment = 3;
#MySQL不支持初始值设置,但是可以使用其他方法
本来不用插入id的值,但是我插入一个我想要的初始值,以后自增长的序号就从我插入的初始值开始
特点:
	1.标识列不一定必须和主键搭配,但是要求是一个key
	2.一个表中至多有一个标识列
	3.标识列的类型只能是数值型,一般用int
	4.标识列可以通过SET auto_increment_increment = 3设置步长,可以通过手动插入值,设置起始值
#二、修改表时设置标识列
	ALTER TABLE tab_identity MODIFY COLUMN id int PRIMARY KEY AUTO_INCREMENT;
#二、修改表时删除标识列
	ALTER TABLE tab_identity MODIFY COLUMN id int PRIMARY KEY;

十一、TCL

介绍&概念
Transaction Control Language 事务控制语言
事务:
	一条或者一组(很条)sql语句组成一个执行单元,这个执行单元要么全部执行,要么全都不执行。
经典案例:转账
	张三丰  1000
	郭襄    1000
	updateset 张三丰的余额 = 500 where name = '张三丰';
	updateset 郭襄的余额 = 1500 where name = '郭襄';
事务的ACID属性:
	1.原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的工作要么都发生,要么都不发生。
	2.一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
	3.隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其他的事务是隔离的,并发执行的各个事务之间不能互相干扰。(还要看隔离级别,有的级别受其它事务影响)
	4.持久性(Durability):持久性是指一个事务一但被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
事务的使用
一、隐式事务:事务没有明显的开启和结束的标记,例如:insertupdatedelete
# 查看自动提交是否开启,ON表示自动提交开启
SHOW variables LIKE 'autocommit';
二、显式事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用
# 只针对当前的事务有效,不是永久的
set autocommit = 0;

三语法:
#步骤一:开启事务
SET autocommit = 0;
START TRANSACTION; #可不写
#步骤二:编写事务中的sql语句(select、insert、update、delete)
	语句一,
	语句二
	...
#步骤三:结束事务
COMMIT;#提交事务
ROLLBACK;#回滚事务

应用:
	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; #提交
    # ROLLBACK; 回滚
数据库的隔离级别
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问:
1.脏读:对于两个事务T1,T2, T1读取了已经被T2更新但是没有提交的字段,之后,若T2回滚,T1读取的内容就是临时且无效的。
2.不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段,之后,T1再次读取同一个字段值就不同了。
3.幻读:对于两个事务T1,T2, T1从一个表中读取了一个字段,然后T2在表中插入了一些新的行。之后,如果T1再次读取同一个表,就会多出几行。

数据库事务的隔离性:
	数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。
MySQL数据库提供的4种事务隔离级别:
		     事务隔离级别	                   脏读	    不可重复读     	幻读
 	读未提交(read-uncommitted)	            是	        是	       是
 	读已提交(不可重复读)(read-committed)    否	       是	       是
 	可重复读(repeatable-read)	            否           否	       是
 	串行化(序列化)(serializable)	         否	         否	        否
读未提交数据(read-uncommitted):允许事务读取未被其他事务提交的变更、脏读,不可重复读和幻读的问题都会出现。
读已提交数据(read-committed):只允许事务读取已经被其它事务提交的变更,可以避免脏读,但是不可重复读和幻读问题仍然可能出现。
可重复读(repeatable-read):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其它事务对这个字段进行刚更新。可以避免脏读和不可重复读,但是幻读的问题仍然存在。
串行化(序列化)(serializable):确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其它事务对该表执行插入,更新和删除操作,所有并发问题都可以解决,但性能十分低下。

了解,Oracle支持两种事务隔离级别:
	Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,如果一个事务正在对某个表执行 DML操作,这时另外一个会话对这个表的记录执行读取操作,则Oracle会去读取回滚段或撤销段中存放的更新之前的记录,而不会象SQL Server一样等待更新事务的结束。
  Oracle的Serializable隔离级别(也称为事务级别的隔离),事务中的读取操作只能读取这个事务开始之前已经提交的数据结果。如果在读取时,其他事务正在对记录执行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经修改的记录(而且是在读取操作所在的事务开始之前存放于回滚段或撤销段的记录),这时读取操作也不会因为相应记录被更新而等待。
  
查看数据库隔离级别:
	# 5.几的版本
	select @@tx_isolation;
	# 8.0以上版本
	select @@transaction_isolation;
设置数据库当前连接隔离级别(session表示当前连接):
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;	
设置数据库系统全局的隔离级别:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#演示savepoint(设置保存点),第一条sql执行,第二条sql回滚
#开启事务
SET autocommit = 0;
START TRANSACTION;
#编写一组事务语句
UPDATE account SET balance = 500 WHERE username = '张无忌';
SAVEPOINT a;#设置保存点
UPDATE account SET balance = 1500 WHERE username = '赵敏';
#结束事务
ROLLBACK TO a; #回滚到保存点

十二、视图

一、概念:
	MySQL从5.0.1版本开始提供视图功能。一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果。
二、应用场景:
	1.多个地方用到同样的查询结果
	2.该查询结果使用的sql语句较为复杂
	
#案例一:查询姓张的学生的姓名和专业名(自己造数据)
SELECT s.stuname, m.`majorName` FROM stuinfo s
INNER JOIN major m ON s.`majorId` = m.id
WHERE s.stuname LIKE '张%';

#创建视图
CREATE VIEW V1 AS  
SELECT s.stuname, m.`majorName` FROM stuinfo s
INNER JOIN major m ON s.`majorId` = m.id;

#有视图后案例一可写成
SELECT * FROM v1 WHERE v1.stuname LIKE '张%';

三、创建视图
语法:
	create view 视图名 as 查询语句;
# 案例一:查询邮箱中包含a字符的员工名、部门名和工种信息
CREATE VIEW v1 AS 
SELECT e.first_name, d.department_name, j.job_title FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
LEFT JOIN jobs j ON j.job_id = e.job_id
WHERE e.email LIKE '%a%';

四、视图的修改
	1.create or replace view v1 as + select语句
	2.alter view 视图名 as + select语句
五、删除视图
	drop view 视图名,视图名...;
六、查看视图
	1.SHOW CREATE VIEW v1\G;#在命令行直接使用时后面加   反斜杠大G作用是格式化输出
	2.DESC 视图名;
七、视图的更新(更改视图中的数据,不推荐直接对视图进行操作)
	# 查看视图
    SELECT * FROM v1;
    1.插入
    # 创建视图
    CREATE OR REPLACE VIEW v1 AS 
    SELECT e.last_name,e.email,e.salary*12*(1 + ifnull(e.commission_pct, 0)) annual_salary FROM employees e;
    #插入失败,annual_salary在原表中没有
    INSERT INTO v1 VALUES ('张飞', '[email protected]', 1000000);

    # 创建视图
    CREATE OR REPLACE VIEW v1 AS 
    SELECT e.last_name,e.email FROM employees e;
    #插入成功,并且修改了原表的数据
    INSERT INTO v1 VALUES ('张飞', '[email protected]');

    2.修改
    UPDATE v1 SET last_name = '张无忌' WHERE last_name = '张飞';
    3.删除
    DELETE FROM v1 WHERE last_name = '张无忌';
    4.以下情况视图不可更新:
    	包含以下关键字的SQL:分组函数、distinctgroup byhavingunion或者union all
    	常量视图
    	select 中包含子查询
    	join
    	from一个不能更新的视图
    	where 子句的子查询引用了from子句中的表

十三、存储过程

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

#一、系统变量
说明:变量由系统提供,不是用户定义,属于服务器层面
语法:
	1.查看所有系统变量的全局变量:SHOW GLOBAL variables;
	2.查看所有系统变量的会话变量:SHOWSESSION】 variables;
	3.查看满足条件的部分变量:show global |SESSION】 variable like '';
	4.查看指定的某个系统变量的值:select @@global|session.系统变量名;
	5.为某个系统变量赋值:
		(1set global|session.系统变量名 =;2set @@global|session.系统变量名 =;
注意:
	1.如果是全局级别(所有连接都有效),则需要加上global,如果是会话级别(只对当前连接有效)则需要加上session,不写则默认session#全局变量
	作用域:所有设置重启后失效(重启会重新初始化),即全局变量设置不能跨重启。
#会话变量
	作用域:仅仅针对当前会话(连接)有效
#二、自定义变量
    说明:变量是用户自定义的,不是系统提供的
    使用步骤:
        声明 -> 赋值 -> 使用(查看、比较、运算等)
    #用户变量
        作用域:针对当前会话(连接)有效,同于会话变量的作用域
        #1.声明(声明时必须初始化)
        SET @用户变量名=;SET @用户变量名:=;SELECT @用户变量名:=#2.赋值(更新用户变量的值)
        方式一:通过set或者select
            SET @用户变量名=;SET @用户变量名:=;SELECT @用户变量名:=值
        方式二:通过select INTO
            SELECT 字段 INTO @变量名 FROM;

        #3.使用(查看用户变量的值)
        SELECT @用户变量名;
     #局部变量
     	作用域:定义此变量的begin end 中有效。
     	注意:应用在begin end 中的第一句。
     	#1.声明
        DECLARE 变量名 类型;
        DECLARE 变量名 类型 DEFAULT;

        #2.赋值
        方式一:通过set或者select
            SET 局部变量名=;SET 局部变量名:=;SELECT @局部变量名:=值
        方式二:通过select INTO
            SELECT 字段 INTO 局部变量名 FROM;

        #3.使用
        SELECT 局部变量;
存储过程
含义:一组预先编译好的SQL语句的集合,也可以理解成批处理语句。
好处:
	1.提高代码的重用性
	2.简化操作
	3.减少了编译次数并且减少了与数据库服务器的连接次数,提高了效率
创建语法:
	CREATE PROCEDURE 存储过程名(参数列表)
	BEGIN 
		存储过程体(一组合法的SQL语句)
	END;
	注意:
		1.参数列表包含三部分:
            参数模式    参数名    参数类型
            例如
            IN	      stuname     varchar(20)
            参数模式:
            IN : 入参
            OUT : 出参
            INOUT : 既是入参也是出参
        2.如果存储过程体只有一句话,begin end 可以省略
        存储过程中的每条SQL语句的结尾要求必须加分号。
        存储过程的结尾可以使用 delimiter 重新设置
调用语法:
	call 存储过程名(参数列表);
	
案例一:空参列表
    # 插入admin表5条数据
    delimiter $
    CREATE PROCEDURE myp1 () 
    BEGIN 
        INSERT INTO admin (username, password) VALUES 
        ('Tom0', '00000'),
        ('Tom1', '00000'),
        ('Tom2', '00000'),
        ('Tom3', '00000'),
        ('Tom4', '00000');
    END $

    # 调用
    CALL myp1() $
案例二:创建带in模式参数的存储过程
    # 根据女神名,查询对应的男神名
    CREATE PROCEDURE myp2(IN beautyName varchar(20))
    BEGIN
        SELECT * FROM boys b RIGHT JOIN beauty be 
        ON b.id = be.boyfriend_id
        WHERE be.name = beautyName;
    END $
    # 调用
    CALL myp2() $
    
    # 创建存储过程,实现验证用户是否登录成功
    CREATE PROCEDURE myp3(IN username varchar(20), IN userPassword varchar(20))
    BEGIN
        # 变量的声明并初始化
        DECLARE myResult int DEFAULT 0;

        SELECT count(*) INTO myResult
        FROM admin a WHERE a.username = username 
        AND a.password = userPassword;

        # 变量打印
        SELECT IF(myResult > 0, '成功', '失败') AS 查询结果;
    END $
    CALL myp3('Tom', '00000');
案例三:创建带out模式的存储过程
    # 根据女神名返回对应的男神名
    CREATE PROCEDURE myp4(
        IN beautyName varchar(20),
        OUT boyName varchar(20)
    )
    BEGIN 
        SELECT b.`boyName` INTO boyName FROM boys b 
        LEFT JOIN beauty be ON b.id = be.boyfriend_id
        WHERE be.name = beautyName;
    END $

    CALL myp4('周芷若', @bName) $
    SELECT @bname$
    
    # 根据女神名,返回对应的男神名和男神魅力值
    CREATE PROCEDURE myp5(
        IN beautyName varchar(20),
        OUT boyName varchar(20),
        OUT userCP int
    )
    BEGIN
        SELECT b.`boyName`, b.userCP INTO boyName, userCP FROM boys b 
        LEFT JOIN beauty be ON b.id = be.boyfriend_id
        WHERE be.name = beautyName;
    END $

    CALL myp5('周芷若', @bName, @userCP)$
    SELECT @bName, @userCP$
案例三:创建带inout模式的存储过程
    # 传入a和b两个值,最终a和b都翻倍并返回
    CREATE PROCEDURE myp6(INOUT a int, INOUT b int) 
    BEGIN
        SELECT a*2, b*2 INTO a, b; 或者 SET a = a * 2; SET b = b * 2;
    END $
    SET @a := 2 $
    SET @b := 3 $
    CALL myp6(@a, @b) $
    SELECT @a, @b $
    
删除存储过程:
	语法:drop procedure 存储过程名;
查看存储过程:
	show create procedure 存储过程名;

十四、函数

定义:一组事先编译好的SQL语句的集合,理解成批处理语句。
好处:	
	1、提高代码重用性
	2、简化操作
	3、减少了编译次数并减少了和数据库服务器连接的此数,提高了效率
和存储过程的区别:
	存储过程:可以有0个返回,也可以有多个返回。适合做插入,批量更新。
	函数:有且仅有1个返回,适合做处理数据后返回一个结果。
创建语法:
	create function 函数名(参数列表) returns 返回类型
	begin
		函数体
	end
注意事项:
	1、参数列表包含两部分:参数名,参数类型
	2、函数体:肯定会有return语句,如果没有会报错,如果return语句没有放在函数体的最后也不会报错,但是不建议。
	3、函数体只有一句话,则可以省略begin end
	4、使用 delimiter 设置结束标记
调用语法:
	select 函数名(参数列表)
无参有返回:
    #  案例一:返回公司的员工个数
    CREATE FUNCTION myf1() RETURNS int
    DETERMINISTIC
    BEGIN 
        DECLARE c int DEFAULT 0; # 定义变量
        SELECT count(*) INTO c FROM employees;
        RETURN c;
    END $
    # 调用
    SELECT myf1() $
有参有返回:
	# 案例一:根据员工名返回他的工资
    CREATE FUNCTION myf2(empName varchar(20)) RETURNS double 
    DETERMINISTIC
    BEGIN
        SET @sal := 0; 
        SELECT salary INTO @sal 
        FROM employees 
        WHERE last_name = empName;

        RETURN @sal;
    END $
    # 调用
    SELECT myf2('chen') $
    
    # 案例二:根据部门名,返回该部门的平均工资
    CREATE FUNCTION myf3(depName varchar(20)) RETURNS double
    DETERMINISTIC
    BEGIN
        DECLARE c double;

        SELECT avg(e.salary) INTO c FROM employees e LEFT JOIN departments d 
        ON e.department_id = d.department_id
        WHERE d.department_name = depName;

        RETURN c;
    END $
	# 调用
    SELECT myf3('Adm') $
查看函数:
	SHOW CREATE FUNCTION myf3;
删除函数:
	DROP FUNCTION 函数名;

十五、流程控制结构

顺序结构:程序从上往下依次执行
分支结构:程序从两条或多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码
分支结构
if函数
功能:实现简单双分支
语法:if(表达式1, 表达式2, 表达式3);
执行顺序:如果表达式1TRUE,则返回表达式2的值,如果表达式1的值为false,则返回表达式3的值。
应用:任何地方
case结构
应用场景:
	1、类似于Java中的switch语句,一般用于实现等值判断。
		语法:
			case 变量|表达式|字段
			when 要判断的值 then 返回的值1
			when 要判断的值 then 返回的值2
			...
			else 要返回的值n
			end
		作为单独语句用的语法:
			case 变量|表达式|字段
			when 要判断的值 then 语句1when 要判断的值 then 语句2...
			else 语句n
			end case;
	2、类似于Java中的多重if语句,一般用于实现区间判断。
		语法:
			case 
			when 要判断的条件1 then 返回的值1
			when 要判断的条件2 then 返回的值2
			...
			else 要返回的值n
			end
		作为单独语句用的语法:
			case 
			when 要判断的条件1 then 语句1;
			when 要判断的条件2 then 语句2;
			...
			else 语句n
			end case;
特点:
	1、可以作为表达式,嵌套在其他语句中使用,可以放在 begin end 中或者放在begin end 的外面
	2、可以作为独立的语句使用,只能放在begin end3、如果when 中的值满足或者条件成立,则执行对应的then 后面的语句,并且结束case,如果不满足就执行else中的语句。
	4else 可以省略,如果else省略了,并且所有when条件都不满足,则返回null
案例:
	# 1、创建存储过程,根据传入的成绩来显示等级,例如传入的成绩在,90-100,显示A;80-90,显示B;60-80,显示C;否则显示D.
    CREATE PROCEDURE mypro1(IN g int)
    BEGIN
        CASE 
            WHEN g <= 100 AND g >= 90 THEN SELECT 'A';
            WHEN g >= 80 THEN SELECT 'B';
            WHEN g >= 60 THEN SELECT 'C';
            ELSE SELECT 'D';
        END CASE;
    END $
    CALL mypro1(99) $
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 myTest(score int) RETURNS varchar(1)
DETERMINISTIC
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 myTest(92) $
循环结构
应用场景:必须放在begin end 中,也是就要有函数或者存储过程。
分类:
	whilelooprepeat
循环控制:
	iterate 类似 Java 的 continue (继续),结束本次循环,继续下一次;
	leave 类似于 Java 的 break (跳出),结束当前所在循环。
1while
语法:
	【标签:】 while 循环条件 do
		循环体;
	end while 【标签】;
案例:
	# 1、批量插入,根据次数插入admin表 (没有返回,用存储过程)
    CREATE PROCEDURE t_while1(IN whileCount int)
    BEGIN 
        DECLARE i int DEFAULT 1;
        WHILE i <= whileCount do 
        INSERT INTO admin (username,`password`) VALUES (concat('Rose', i), '666');
        SET i = i + 1; 
        END WHILE;
    END $
    # 调用
    CALL t_while1(20) $
    
    # 2、添加leave语句,批量插入,根据次数插入admin表 (没有返回,用存储过程),如果次数>20则停止
    CREATE PROCEDURE t_while2(IN wCount int)
    BEGIN 
        DECLARE i int DEFAULT 1;
        a: WHILE i <= wCount do 
            INSERT INTO admin (username, `password`) VALUES (concat('xiaohu', i), '000000');
            IF i > 20 THEN LEAVE a;
            END IF;
            SET i = i + 1;
        END WHILE a;
    END $
	# 调用
    CALL t_while2(30) $
    
    # 3、添加iterate语句,批量插入,根据次数插入admin表 (没有返回,用存储过程),只插入i为偶数的
    CREATE PROCEDURE t_while3(IN wCount int)
    BEGIN 
        DECLARE i int DEFAULT 0;
        a: WHILE i <= wCount do 
            SET i = i + 1;
            IF i % 2 != 0 THEN ITERATE a;
            END IF;
            INSERT INTO admin (username, `password`) VALUES (concat('xiaohu', i), '000000');
        END WHILE a;
    END $
	# 调用
    CALL t_while3(20) $
    
2loop
语法:
	【标签:】 loop
		循环体;
	end loop 【标签】;
应用:可以用来模拟简单的死循环
3repeat
语法:
	【标签:】 repeat 
		循环体;
	until 结束循环的条件
	end repeat 【标签】;
流程控制经典案例
已知表 stringcontent,其中字段:
id 自增长
content varchar(20)
向表中插入指定条数的,随机的字符串

CREATE PROCEDURE randstrInsert(IN iCount int)
BEGIN 
	DECLARE i int DEFAULT 1; #变量i表示插入次数
	DECLARE str varchar(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
	DECLARE startIndex int DEFAULT 1; # 起始索引
	DECLARE len int DEFAULT 1; # 截取字符串的长度
	WHILE i <= iCount do 
		SET len = floor(rand()*(20-startIndex+1) + 1); # 产生一个随机整数,代表截取长度,1-(26-startIndex+1) 
		SET startIndex = floor(rand()*26+1); # 产生一个随机整数,代表起始索引1-26
		INSERT INTO stringcontent (content) VALUES (substring(str, startIndex, len));
		SET i=i+1; # 循环变量更新
	END WHILE;
END $
# 调用
CALL randstrInsert(10)$

你可能感兴趣的:(MySQL,mysql,数据库)