MYSQL学习——万字长文总结(代码+笔记+案例)

MySQL教程

学习之前的声明:本文是根据尚硅谷的教学视频自己整理记录的,全部内容均为自己手打,既是对自己所学的总结也是今后知识回顾的依据,文中涉及到的所有代码和数据库我会以网盘链接的方式分享。其中源代码在学习笔记这一栏里面。

链接:https://pan.baidu.com/s/1xuMGazwWyJkhy5e2BXBkNQ
提取码:aww2

一、数据库和SQL概述

  • 为什么需要数据库?

    • 实现数据持久化
    • 使用完整的管理系统统一管理,易于查询
  • 数据库的概念:

    • DB:数据库(database):存储数据的“仓库”。它保存了一系列有组织的数据。
    • **DBMS:**数据库管理系统(Database Management System)。数据库是通过DBMS创 建和操作的容器
    • SQL:结构化查询语言(Structure Query Language):专门用来与数据库通信的语言。

在这里插入图片描述

  • SQL的优点

    • 不是某个特定数据库供应商专有的语言,几乎所有 DBMS都支持SQL
    • 简单易学
    • 虽然简单,但实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。
  • SQL语言分类

    • **DML(Data Manipulation Language)😗*数据操纵语句,用于添 加、删除、修改、查询数据库记录,并检查数据完整性
      • INSERT:添加数据到数据库中
      • UPDATE:修改数据库中的数据
      • DELETE:删除数据库中的数据
      • SELECT:选择(查询)数据 ,SELECT是SQL语言的基础,最为重要
    • DDL(Data Definition Language):数据定义语句,用于库和 表的创建、修改、删除。
      • CREATE TABLE:创建数据库表
      • ALTER TABLE:更改表结构、添加、删除、修改列长度
      • DROP TABLE:删除表
      • CREATE INDEX:在表上建立索引
      • DROP INDEX:删除索引
    • **DCL(Data Control Language)😗*数据控制语句,用于定义用户的访问权限和安全级别。
      • GRANT:授予访问权限
      • REVOKE:撤销访问权限
      • COMMIT:提交事务处理
      • ROLLBACK:事务处理回退
      • SAVEPOINT:设置保存点
      • LOCK:对数据库的特定部分进行锁定

在这里插入图片描述

二、MySQL安装与使用

  • MySQL产品的特点

    • MySQL数据库隶属于MySQL AB公司,总部位于瑞典,后被oracle收购。
    • •优点:
      • 成本低:开放源代码,一般可以免费试用
      • 性能高:执行很快
      • 简单:很容易安装和使用
  • DBMS分为两类:

    • 基于共享文件系统的DBMS (Access )
    • 基于客户机——服务器的DBMS (MySQL、Oracle、SqlServer)

2.1 MySql的安装

可以参考我的另一篇博文:

https://blog.csdn.net/zxzx666/article/details/119534320?spm=1001.2014.3001.5501

2.2 MySQL的启动和停止

  • 方式一:通过计算机管理方式

    右击计算机—管理—服务—启动或停止MySQL服务

  • 方式二:通过命令行方式

    必须以管理员身份启动(cmd)

    启动:net start mysql 服务名

    停止:net stop mysql 服务名

在这里插入图片描述

2.3 MySQL服务的登录和退出

方式一:直接使用mysql客户端登录

​ 仅限于root用户

方式二:利用命令行方式

  • 登录

    mysql -h 主机名 -P 端口号 -u 用户名 -p密码 (-p密码之间没有空格)

    本机登录的话可以简写为:mysql -u 用户名 -p密码

  • 退出

    exit 或者 ctrl+c

在这里插入图片描述

2.4 MySQL的常见命令

1.查看当前所有的数据据
  show databases;
  
2.打开指定的库
  use 库名;
  
3.查看当前库的所有表
  show tables;
  
4.查看其它库所有表
  show tables from 库名;
  
5.创建表
  create table 表名(
  	列名  列类型
  	列名  列类型
  	。。。 	 
  );
  
6.查看表结构
  desc  表名;
  
7.删除表
  drop table 表名;
  
8.查看表中所有记录
  select * from 表名;
  
9.向表中插入记录
  insert into 表名(列名列表) values(列对应的值的列表)
  注意:插入varchar或date型的数据要用 单引号 引起来
  例如:insert into customer(id,name,age) values('1002','Jerry',20);
  
10.修改记录
  update 表名 set 列1-列1的值,列2=列2的值  where ...
  例如:update customer set age=22,name='Tom'  where id='1002';
  
11.删除记录
  delete from 表名 where...
  例如:delete from customer where id='1001';
  
12.查询所有列
  select * from 表名;
  
13.查询特定的列
  select 列名1,列名2,.... from 表名;
  
14.对查询的数据进行过滤
  使用where子句
  例如: select id,name,age from customer  where age>20;
  
15.运算符
  例如: select * from customer  where salary>=2000 and salary<=3000;
        select * from customer  where salary between 2000 and 3000;
         
16.查看服务器版本
  方式一:登录到mysql服务端
  select version();
  方式二:没有登录到mysql服务端
  mysql  --version
  或
  mysql  -V

2.5 MySQL的语法规范

1.不区分大小写,但建议关键字大写,表明、列表小写
2.每条命令最好用分号结尾
3.每条命令根据需要,可以进行换行、缩进,直到输完命令以“;”结尾
4.注释
    单行注释: #注释文字
    单行注释: --  注释文字
    多行注释: /* 注释文字 */

三、DQL(Data Query Language)数据查询语言

3.1 基础查询

#进阶1:基础查询
/*
语法:
select 查询列表 from 表名;

类似于:Sysytem.out.println(打印东西);

特点:
1.查询列表可以是:表中的字段、常量值、表达式、函数
2.查询的结果是一个虚拟的表格

假如存在一个字段和关键字相同,可以添加着重号区分,例如 `NAME`
*/


USE myemployees;

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


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

/*
按F12可以格式化,前提是要选中要格式化的部分,例如
SELECT 
  last_name,
  salary,
  email 
FROM
  employees ;
*/


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


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


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


#6.查询函数
SELECT VERSION();#查询该函数,并且得到其返回值


#7.为字段起别名
/*
①便于理解
②如果要查询的字段有重名的情况,使用别名可以区分开来
*/
#方式一:使用AS
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 AS 'out put'  FROM employees;


#8.去重
#使用关键字  DISTINCT

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


#9.+号的作用
/*
JAVA中的+号:
①运算符,两个操作数都为数值型
②连接符,只要有一个操作数为字符串

mysql中的+号:
仅仅只有一个功能:运算符。

select 100+90;两个操作数都为数值型,则做加法运算
select '123'+90;其中乙方为字符型,试图将字符型数值转换成数值型
				  如果转换成功,则继续做加法运算;
select 'john'+90;		  如果转换失败,则将字符转换成0
select null+90;			  一方为null,结果肯定为null
*/


#10.concat函数
/*
功能:拼接字符
select concat(字符1,字符2,....)
*/


#11.ifnull函数
/*
功能:判断某个字段或者表达式是否为null,如果为null繁殖指定的值,否则返回原来的值
select if(commission_pct,0) from employees;
*/


##12.isnull函数
#功能:判断某字段或者表达式是否为null,如果是,返回1,否则返回0


#案例1:查询员工名和姓连接成一个字段,并显示为姓名
SELECT CONCAT(last_name,first_name) AS 姓名 FROM employees;
#案例2:显示出表employees的全部列,各个列之间用逗号连接,列头显示为OUT_PUT
SELECT 
	CONCAT(`employee_id`,`first_name`,`last_name`,IFNULL(`commission_pct`,0)) AS OUT_PUT

FROM
	employees;

3.2 条件查询

#进阶2:条件查询
/*

语法:
	select
		查询列表
	from
		表名
	where
		筛选条件;
		
分类:
	一、按条件表达式筛选
	
	条件运算符:> < = != <> >= <=
	
	二、按逻辑表达式筛选
	
	逻辑运算符:
	作用:用于连接表达式条件
		&&    ||   !
		and   or  not
	
	三、模糊查询
		like
		between  and
		in
		is null
*/

#一、按条件表达式筛选

#案例1:查询工资>12000的员工信息

SELECT * FROM employees WHERE salary>12000;

#案例2:查询部门编号不等于90号的员工名和部门编号

SELECT last_name,department_id FROM employees WHERE department_id!=90;



#二、按按逻辑表达式筛选

#案例1:查询工资在10000到20000之间的员工名、工资以及奖金
SELECT 
  last_name,
  salary,
  commission_pct 
FROM
  employees 
WHERE salary >= 10000 
  AND salary <= 20000 ;

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


#三、模糊查询
/*
like
特点:
①一般和通配符搭配使用
	通配符:
	% 任意多个字符,包含0个字符
	_ 任意单个字符,跟位置有关

between  and
in
is null
*/

#1.like
#案例1:查询员工名中包含字符a的员工信息
SELECT * FROM employees WHERE last_name LIKE '%a%';
#案例2:查询员工名中第三个字符为n,第五个字符为l的员工名和工资
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 '_$_%' ESCAPE '$';


#2.between  and
/*
①使用between and可以提高语句的简洁度
②包含了临界值
③两个临界值不能颠倒
*/
#案例:查询员工编号在100到120之间的员工信息
SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;



#3.in
/*
含义:判断某字段的值是否属于in列表中的某一项
特点:
	①使用in提高语句简洁度
	②in列表的值类型必须一致或者兼容
*/
#案例:查询员工的工种编号是 IT_PROG AD_VP AD_PRES中的一个员工名和工种编号
SELECT last_name,job_id FROM employees WHERE job_id IN('IT_PROG','AD_VP','AD_PRES');



#4. is null
/*
=或者<>不能用于判断null值
is null 或 is not 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;


#安全等于 <=>
#案例1:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct FROM employees WHERE commission_pct <=> NULL;
#案例2:查询工资为12000的员工信息
SELECT last_name,salary FROM employees WHERE salary <=> 12000;


# is null  pk   <=>
IS NULL:仅仅可以判断NULL值
<=>:既可以判断NULL值又可以判断普通的数值,但不常用

【例题】

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

#例题2:查询没有奖金且工资小于10000的salary,last_name
SELECT 
  last_name,
  salary 
FROM
  employees 
WHERE (commission_pct IS NULL) 
  AND (salary < 10000) ;

#例题3:查询emplees表中,job_id不为'IT'或者工资为12000的员工信息
SELECT
  *
FROM
  employees
WHERE
  job_id<>'IT'
  OR
  salary=12000;
  
#例题4:经典面试题:
#试问:select * from employees;和
#select* from employees where commission_pct like '%%'and last_name like '%%';
#结果是否一样?并说明原因

#答案:不一样:如果判断的字段没有null值,则一样,否则不一样

3.3 排序查询

#进阶3:排序查询

/*
引入:
	select * from employees;

语法:
	select 查询列表
	from  表
	【where 筛选条件】
	order by  排序列表  【asc |desc】
	
特点:
	1、asc代表的是升序,desc代表的是降序
	   如果不写,默认是升序
	2、order by子句中可以支持单个字段、多个字段、表达式、函数、别名
	3、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 ASC;

#案例4:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT *sa,lary*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) AS 字节长度,last_name,salary
FROM employees  ORDER BY 字节长度 ASC;

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

【例题】

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

#2.选择工资不在8000到17000的员工的姓名和工资,按工资降序
SELECT last_name,salary
FROM employees
WHERE salary<8000 OR salary>17000
#where salary not between 8000 and 17000
ORDER BY salary DESC;

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

3.4 常见函数

常见函数介绍总结

#进阶4:常见函数

/*

功能:类似于java的方法,将我们的一组逻辑语句封装在方法体中,对外暴漏方法名

优点:1、隐藏了实现细节   2、提高代码的重用性

调用: select 函数名(实参列表) 【from 表】;

特点:①叫什么(函数名)
      ②干什么(函数功能)
      
分类:
      1、单行函数
         如:concat、length、ifnull等
      2、分组函数
         功能:做统计使用,又称为统计函数、聚合函数、组函数
         
 常见函数:
	字符函数:
		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
		datebase
		user
	控制函数:
		if
		case
		
*/

一、字符函数

#一、字符函数

#1.length:获取参数值的字节个数
SELECT LENGTH('john');#4
SELECT LENGTH('张三丰zzzz');#13,一个汉字在utf8中是三个字节

SHOW VARIABLES LIKE '%char%';

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

#3.upper、lower:大小写转换
SELECT UPPER('john');
SELECT LOWER('john');

#4.substr、substring:剪切字符,从1开始
#截取从指定索引处后面所有字符
SELECT SUBSTR('小龙女爱上了杨过',7) AS out_put;#杨过
#截取从指定索引处指定字符长度的字符
SELECT SUBSTR('小龙女爱上了杨过',1,3) AS out_put;#小龙女

#5.instr:返回指定字符串在字符串中首次出现的索引,找不到返回0
SELECT INSTR('杨不悔爱上了殷六侠','殷六侠') AS out_put FROM employees;

#6.trim
#去掉字符串前后空格
SELECT LENGTH(TRIM('   张翠山   ')) AS out_put; 
#去掉字符串前后指定字符,
SELECT TRIM('aa' FROM 'aaaaaaaa张aa翠aaaa山aaaaaaa') AS out_put;

#7.lpad:用指定的字符实现左填充指定长度
SELECT LPAD('殷素素',10,'*') AS out_put;#*******殷素素
SELECT LPAD('殷素素',2,'*') AS out_put;#殷素

#8.rpad:用指定的字符实现右填充指定长度
SELECT RPAD('殷素素',12,'as') AS out_put;#殷素素asasasasa

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

二、数学函数

#二、数学函数

#1.round:四舍五入SELECT ROUND(1.65);
SELECT ROUND(-1.65);
SELECT ROUND(1.74747,3);

#2.ceil:向上取整,返回>=该参数的最小整数
SELECT CEIL(1.1);
SELECT CEIL(-1.1);

#3.floor:向下取整,返回<=该参数的最大整数
SELECT FLOOR(1.1);

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

#5.mod:取余 MOD(a,b): a-a/b*b
SELECT MOD(-10,-3);
SELECT 10%3;

三、日期函数

#三、日期函数

#1.mow:返回当前系统日期
SELECT NOW();

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

#3.curtime:返回当前系统时间,不包含时间
SELECT CURTIME();

#4.可以获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW())AS 年;
SELECT YEAR('2021-08-10') AS 年;
SELECT MONTH(NOW()) AS 月;
SELECT DAY(NOW()) AS 日;
SELECT HOUR(NOW()) AS 小时;
SELECT MINUTE(NOW())AS 分钟;
SELECT SECOND(NOW()) AS 秒;

#5.str_to_date:将日期格式的字符转换成指定的日期
/*
	%Y 四位的年份   	
	%y 2位的年份
	%m 月份(01,02..12)	
	%c 月份(1,2...12)
	%d 日(01,02...)	
	%H 小时(24小时制)	
	%h 小时(12小时制)
	%i 分钟(00,01....)   
	%s 秒(00,01.....)
*/
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');

#6.date_format:将日期转换成字符号
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put;#21年08月10日

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

四、其他函数

#四、其他函数

#1.查看当前版本号
SELECT VERSION();

#2.查看当前数据库
SELECT DATABASE();

#3.查看当前用户
SELECT USER();

五、控制函数

#五、流程控制函数

#1.if函数:if else的效果
SELECT IF(10>5,'大','小');

SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金,呵呵','有奖金,耶')
FROM employees;

#2.case函数的使用1: 类似于switch case的效果,判断的是等值
/*
	case 要判断的字段或表达式
	when 常量1 then  要显示的值1或语句1;
	when 常量2 then  要显示的值2或语句2;
	....
	else 要显示的值n或语句n;
	end
*/

/*
    案例:查询员工工资,要求
		 部门号位=30,显示的工资位1.1倍
		 部门号位=40,显示的工资位1.2倍
		 部门号位=50,显示的工资位1.3倍
		 其它部门,显示的工资为原工资
*/
SELECT department_id,salary 原始工资,
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;

#3.case函数的使用2: 类似于多重if elseif else的效果,判断的是区间
/*
	casewhen 条件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;

3.5 分组函数

#进阶5:分组函数
/*
功能:用作统计使用,又称为聚合函数或统计函数或组函数

分类:
sum 求和、avg 平均值、max 最大值、min 最小值、count 计算个数

特点:   
	1.sum、avg一般用于处理数值型      
	   max、min、count可以处理任何类型         
	2.以上分组函数都会忽略null值        
	3.可以和distinct搭配实现去重运算       
	4.count函数的单独介绍      
	    一般使用count(*)来统计行数         
	5.和分组函数一同查询的字段要求是group by后的字段
*/

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

#2.参数支持哪些类型
SELECT SUM(last_name),AVG(last_name) FROM employees;

SELECT MAX(last_name),MIN(last_name) FROM employees;

SELECT MAX(hiredate),MIN(hiredate) FROM employees;

SELECT COUNT(last_name) FROM employees;

#3.忽略null
SELECT SUM(commission_pct),AVG(commission_pct), FROM employees;

SELECT MAX(commission_pct),MIN(commission_pct) FROM employees;

SELECT COUNT(commission_pct) FROM employees;

#4.和distinct搭配:去重
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;

SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;

#5.count函数的详细介绍
SELECT COUNT(salary) FROM employees;

SELECT COUNT(*) FROM employees;#经常用来统计不为null的行数

SELECT COUNT(1) FROM employees;#括号随意放个常量值,统计行数

/*效率:
MYISAM存储引擎下,count(*)的效率高
INNODB存储引擎下,count(*)和count(1)的效率差不多,比count(字段)要高一些
*/

#6.和分组函数一同查询的字段有限制
SELECT AVG(salary),employee_id FROM employees;#不是一个规格的表格

3.6 分组查询

#进阶6:分组查询
/*
语法:
	select 分组函数,列(要求出现在grooup by的后面)
	from 表
	【where 筛选条件】
	group by  分组的列表
	【order by 子句】

注意:
	查询列表必须特殊,要求是分组函数和group by后出现的字段
	
特点:
	1.分组查询中的筛选条件分为两类
		        数据源		位置	 	  关键字
	分组前筛选	原始表		group by子句前面  where
	分组后筛选	分组后的结果集  group by子句后面  having

	①分组函数做条件肯定是放在having子句中
	②能用分组前筛选的尽量放在where中筛选
	
	2、group by子句支持单个字段分组、多个字段分组(多个字段之间用逗号隔开,
	   没有顺序要求)、表达式或函数分组(用的较少)
	   
        3、也可以添加排序(放在最后)

*/
#引入:查询每个部门的平均工资
SELECT AVG(salary) FROM employees;


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

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


#2.添加分组前的筛选
#案例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;


#3.添加分组后的筛选
#案例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;



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



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



#6.添加排序的分组
#案例:查询每个部门,每个工种的平均工资,并按照工资的高低显示大于10000的平均工资
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;

3.7 连接查询

连接查询介绍

#进阶7:连接查询
/*含义:多表查询,当查询的字段来自多个表时,使用连接查询

笛卡尔乘积现象:表1有m行,表2有n行,结果=n*m行

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

分类:
	按年代分类:	    
		sql92标注:仅仅支持内连接	    
		sql99标准【推荐使用】:支持内连接+外连接(左外连接+右外连接)+交叉连接	
	按功能分类:	    
		内连接:	      
			等值连接	        
			非等值连接	        
			自连接	    
		外连接:	        
			左外连接	        
			右外连接	        
			全外连接	    
			交叉连接
*/
一、sql92语法

1.sql92——内连接——等值连接

/*
	①多表等值连接的结果是多表的交集部分
	②n表连接,至少要写n-1个连接条件
	③多表的顺序没有要求
	④一般需要为表起别名
	⑤可以搭配前面介绍的所有子句使用,比如排序、筛选、分组
*/

#案例1:查询女神和对应的男神名
SELECT `NAME`,boyName FROM boys,beautyWHERE beauty.boyfriend_id=boys.id;

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

#1 为表写别名
/*
	①提高语句的见解读
	②区分多个重名的字段注意:如果为表起别名,则查询的字段不能使用原来的表名去限定
*/
#查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,j.job_title
FROM employees AS e,jobs j
WHERE e.`job_id`=j.`job_id`;

#2 两个表的顺序可以可调换
#案例:查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,j.job_title
FROM jobs j,employees AS e
WHERE e.`job_id`=j.`job_id`;

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

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

#4 可以加分组
#案例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,d.manager_id;

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

#6 可以实现三表连接
#案例:查询员工名、部门名和所在的城市
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;

2.sql92——内连接——非等值连接

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

3.sql92——内连接——自连接

#案例:查询员工名和上级的名称
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`;
二、sql99语法
/*语法:	
	select 查询列表	
	from   表1 别名
	【连接类型】join   表2 别名	
	on    连接条件	
	【where  筛选条件】	
	【group by 分组】	
	【having 筛选条件】	
	【order by 排序列表】

分类:
	内连接(*):inner	
	外连接:		
		左外连接(*):left【outer】		
		右外连接(*): right【outer】		
		全外连接: full【outer】	
		交叉连接:cross
*/

1.sql99——内连接

/*
语法:	
	select 查询列表	
	from 表1 别名	
	inner join 表2 别名	on 连接条件;
	inner join 表3 别名	on 连接条件;
	。。。。

分类:	
	等值连接	
	非等值连接	
	自连接
	
特点:	
	①添加排序分组筛选	
	②inner可以省略	
	③筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读	
	④inner join连接和sql92语法中的等值连接最终实现效果一样,都是多表交集	
*/

#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 last_name LIKE '%e%';

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

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



#2.非等值连接

#查询员工的工资级别
SELECT salary,grade_level
FROM employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`;

#查询每个工资级别的个数>2的个数,并且按照工资级别降序
SELECT COUNT(*),grade_level
FROM employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`
GROUP BY grade_level
HAVING COUNT(*)>2
ORDER BY grade_level DESC;



#3.自连接

#查询员工的名字和上级的名字
SELECT e.last_name,m.last_name
FROM employees e
INNER JOIN employees m
ON e.`manager_id`=m.`employee_id`;

#查询姓名中包含字符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%';

2.sql99——外连接

/*
应用场景:用于查询一个表中有,另外一个表没有的部分

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

#引入:查询男朋友 不再男神表的女神名#左外连接实现
SELECT be.name,bo.*
FROM beauty be
LEFT OUTER JOIN boys bo
ON be.`boyfriend_id`=bo.`id`
WHERE bo.`id` IS  NULL;

#右外连接实现
SELECT be.name,bo.*
FROM boys bo
RIGHT OUTER JOIN beauty be
ON be.`boyfriend_id`=bo.`id
`WHERE bo.`id` IS  NULL;

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

#全外连接
USE girls;
SELECT be.* bo.*
FROM beauty be
FULL OUTER JOIN boys bo
ON be.boyfriend_id=bo.id;

3.sql99——交叉连接

#就是笛卡尔乘积m*n
SELECT be.*,bo.*
FROM beauty be
CROSS JOIN boys bo;
  • sql99 VS sql92
    • 功能:sql99支持的较多
    • 可读性:sql99实现连接条件和筛选条件分离,可读性更高

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

3.8 子查询

  • 子查询简介与分类
#进阶8:子查询

/*
含义:出现在其他语句中的select语句,称为子查询或内查询
      外部的查询语句,成为主查询或外查询
      
分类:
      按子查询出现的位置:
	  select后面:
		仅仅支持标量子查询
	  from后面:
		支持表子查询
	  where或having后面:💥
		标量子查询 (单行)💥
		列子查询   (多行)💥
		行子查询
	  exists后面(相关子查询):
		表子查询
      按结果集行列数不同:
	  标量子查询(结果集只有一行一列)
	  列子查询(结果集只有一列多行)
	  行子查询(结果集只有一行多列)
	  表子查询(结果集一般为多行多列)
*/

子查询中用的最多的就是放在where或having后面的子查询,先着重介绍这一组

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

3.行子查询(多列多行)

特点:
  ①子查询放在小括号内
  ②子查询一般放在条件的右侧
  ③标量子查询,一般搭配着单行操作符使用
    > < >= <= <>
    列子查询一般搭配着多行操作符使用
    in  any/some  all
  ④子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
*/

#1.标量子查询

#案例1:谁的工资比Abel高?
#①查询Abel工资
SELECT salary
FROM employees
WHERE last_name='Abel'
#②查询员工信息,满足salary>①的结果
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 MIN(salary)>(
	SELECT MIN(salary)
	FROM employees
	WHERE department_id=50
);

#非法使用标量子查询——子查询得到的不是一个标量




#2.列子查询(多行子查询)
/*
多行操作符:
	IN/MOT IN :等于列表中的任意一个
	ANY/SOME  :和子查询返回的某一个值比较
		   (比如A>ANY/SOME(子查询),等于>min)
	ALL       :和子查询返回的所有值比较
		   (比如A>ALL(子查询),等于>max)
*/

#案例1:返回location_id是1400或1700的部门中的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id IN(
	SELECT department_id
	FROM departments
	WHERE location_id IN(1400,1700)
);
#或者
SELECT last_name
FROM employees
WHERE department_id =ANY(
	SELECT department_id
	FROM departments
	WHERE 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';
#或者
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary<(
	SELECT MIN(salary)
	FROM employees
	WHERE job_id='IT_PROG'
)AND job_id <>'IT_PROG';




#3.行子查询(结果集一行多列或多行多列)(用的较少)

#案例:查询员工编号最小并且工资最高的员工信息
SELECT *
FROM employees
WHERE employee_id=(
	SELECT MIN(employee_id)
	FROM employees
)AND salary=(
	SELECT MAX(salary)
	FROM employees
);
#利用行子查询可以简化为
SELECT *
FROM employees
WHERE (employee_id,salary)=(
	SELECT MIN(employee_id),MAX(salary)
	FROM employees
);

其余三种情况的子查询

#二、select后面

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



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

	

#四、exists后面(相关子查询)
/*
语法:
exists(完整的查询语句)
结果:
0或者1

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

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

【例题1】

#【例题】

#例题1:查询zlotkey的部门
SELECT department_name
FROM departments d
WHERE department_id IN(
		SELECT department_id	
		FROM employees e	
		WHERE e.`last_name`='zlotkey'
);

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

#例题3:查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资
SELECT e.employee_id,e.last_name,e.salary,e.`department_id`
FROM employees e
INNER JOIN (	
		SELECT AVG(salary) ag,department_id	
		FROM employees	
		GROUP BY department_id
)ag_dep
ON e.`department_id`=ag_dep.department_id
WHERE salary>ag_dep.ag;

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

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

#例题6:查询管理者是K_ing的员工姓名和工资
SELECT last_name,salary
FROM employees e
WHERE e.`manager_id` IN(	
		SELECT employee_id	
		FROM employees	
		WHERE last_name='K_ing'
);

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

【例题2】

# 1. 查询工资最低的员工信息: last_name, salary

#①查询最低的工资
SELECT MIN(salary)
FROM employees;

#②查询last_name,salary,要求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

#②查询①结果上的最低平均工资
SELECT MIN(ag)
FROM (	
		SELECT AVG(salary) ag,department_id	
		FROM employees	
		GROUP BY department_id
) ag_dep;

#③查询哪个部门的平均工资=②
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 AVG(salary),department_id
FROM employees
GROUP BY department_id

#②求出最低平均工资的部门编号
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) 
LIMIT 1;

#③查询部门信息
SELECT *
FROM departments
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

#②求出最低平均工资的部门编号
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
 LIMIT 1;

#③查询部门信息
SELECT d.*,ag
FROM departments d
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;

# 4. 查询平均工资最高的 job 信息
#①查询最高的job的平均工资
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1

#②查询job信息
SELECT * 
FROM jobs
WHERE job_id=(	
		SELECT job_id	
		FROM employees	
		GROUP BY job_id	
		ORDER BY AVG(salary) DESC	
		LIMIT 1
);

# 5. 查询平均工资高于公司平均工资的部门有哪些
#①查询平均工资
SELECT AVG(salary)
FROM employees

#②查询每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id

#③筛选②结果集,满足平均工资>①
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)>(
		SELECT AVG(salary)	
		FROM employees
);

# 6. 查询出公司中所有 manager 的详细信息.
#①查询所有manager的员工编号
SELECT DISTINCT manager_id
FROM employees

#②查询详细信息,满足employee_id=①
SELECT *
FROM employees
WHERE employee_id =ANY(	
		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     department_id 
FROM    employees 
GROUP BY department_id 
ORDER BY AVG(salary) DESC 
LIMIT 1

 #②将employees和departments连接查询,筛选条件是①    
SELECT         last_name, d.department_id, email, salary     
FROM        employees e        
INNER JOIN departments d             
ON d.manager_id = e.employee_id    
WHERE d.department_id = (
		SELECT   department_id        
		FROM   employees        
		GROUP BY department_id        
		ORDER BY AVG(salary) DESC       
		LIMIT 1
) ;

3.9 分页查询

/*
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
语法:	
		select 查询列表	
		from 表	
		【join type join 表2	on 连接条件	
		where 筛选条件	
		group by 分组字段	
		having 分组后的筛选	
		order by 排序的字段】	
		limit 【offset】,size;		

		offset要显示条目的起始索引(起始索引从0开始)	
		size要显示的条目个数	
特点:	
		①limit语句放在查询语句最后	
		②公式:要显示的页数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;

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

3.10 union联合查询

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

语法:
		查询语句1	union	
		查询语句2	union	
		.....;	
		
应用场景:	
		要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时	

注意事项:	
		1.要求多条查询语句的查询列数是一致的	
		2.要求多条查询语句的查询的每一列的类型和顺序最好一致,不然会出现表头和信息对不上的情况	
		3.union关键字默认去重,如果使用union all可以避免去重
*/

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

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

四、DML(Data Manipulation Language)数据操作语言

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

4.1 插入语句

#一、插入语法
#方式一:经典的插入
/*
语法:
		insert into 表名(列名,。。。)	
		values(值1,。。),	
		values(值1,。。),	
		。。。。。。。。。。;
*/
#1.插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐艺昕','女','1990-4-23','18890029292',NULL,2);

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

#方式二:INSERT INTO beauty(id,NAME,sex,borndate)
VALUES(14,'赵丽颖','女','1990-4-23');

#3.列的顺序可以颠倒,值对应就行

#4.列数和值的个数必须一致

#5.可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致



#方式二:
/*语法:	
		insert into 表名	
		set 列名=值,列名=值
*/
INSERT INTO beauty
SET id=20,NAME='小乔';


#两种方式比较
/*
1.方式一支持插入多行,方式二不支持
2.方式一子查询,方式二不支持
*/

INSERT INTO beauty(id,NAME)
SELECT id,boyName
FROM boys WHERE id<3;

4.2 修改语句

#二、修改语句
/*
1.修改单表的记录💥
语法:
		update 表名	
		set 列=新值,列=新值,......	
		where 筛选条件;  (不加筛选条件就会修改掉整列)

2.修改多表的记录【补充】
语法:  
		sql92语法:	
				update 表1  别名,表2 别名	
				set 列=值,。。。。	
				where 链接条件	
				and 筛选条件  
		sql99语法:	
				update 表1 别名	
				inner|left|right join 表2 别名	
				on 连接条件
				set 列=值,......	
				where 筛选条件
*/
#1.修改单表的记录

#案例1:修改beauty表中姓唐的女神的电话为123456789901
UPDATE beautySET phone='12345678901'
WHERE NAME LIKE '唐%';

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


#2.修改多表的记录

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

#案例2:修改没有男朋友的女神的男朋友的编号都为2号
UPDATE boys bo
INNER JOIN beauty be
ON bo.`id`=be.`boyfriend_id`
SET be.`id`=2
WHERE be.`boyfriend_id` IS NULL;

4.3 删除语句

#三、删除语句
/*
方式一:delete
语法:    
		1.单表的删除:的💥	
		delete from 表名 where 筛选条件    
		2.多表的删除【补充】	delete 表1的别名,表2的别名	
		from 表1 别名	
		inner|left|right 表2 别名	
		on 连接条件	where筛选条件;	

方式二:truncate(全部清空)
语法:	truncate  table 表名;
*/

#方式一:delete
#1.单表删除
#案例:删除手机号以9结尾的女神信息
DELETE FROM beauty WHERE phone LIKE '%9';

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

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


#方式二:truncate语句
TRUNCATE TABLE boys;



#delete  PK  truncate 【面试题】
/*
1.delete可以加where条件,truncate不能加
2.truncate删除,效率高一点点
3.假如要删除的表中有自增长列,  如果用delete删除后,再插入数据,自增长从断点开始  如果用truncate删除后,再插入数据,自增长从1开始
4.truncate删除没有返回值,delete删除有返回值
5.truncate删除不能回滚,delete删除可以回滚
 */

五、DDL(Data Define Language)数据定义语言

5.1 库和表的管理

#DDL语言
/*
数据定义语言:库和表的管理

一、库的管理    
		创建、修改、删除
二、表的管理    
		创建、删除、修改         
				创建:create     
				删除:drop     
				修改:alter
*/

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

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

#2.库的修改(一般不会修改库)
#更改库的字符集
ALTER DATABASE books CHARACTER SET gbk;

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



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

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

#案例:创建表aurthor
CREATE TABLE IF NOT EXISTS author(	
		id INT,#编号	
		au_name VARCHAR(20),#名字	
		nation VARCHAR(20)#国籍
);
DESC author;


#2.表的修改
/*
alter table 表名 add|drop|modify|change column 列名 【列类型 约束】;
*/
#①修改列名
ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME;

#②修改列的类型或约束
ALTER TABLE book MODIFY COLUMN pubDate TIMESTAMP;

#③添加新列
ALTER TABLE author ADD COLUMN annual DOUBLE;

#④删除列
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 新库名;


#4.表的复制
INSERT INTO author VALUES
(1,'村上村数','日本'),
(2,'莫言','中国'),
(3,'金庸','台湾');


#1.仅仅复制表的结构
CREATE TABLE copy LIKE author;

#2.复制表的结构+数据
CREATE TABLE copy2SELECT* FROM author;

#3.只复制部分数据
CREATE TABLE copy3SELECT id,au_name,nationFROM authorWHERE au_name='金庸';

#4.仅仅复制部分结构
CREATE TABLE copy4SELECT id,au_nameFROM authorWHERE 0;

【例题】

#1.创建表dept1
/*
NAME	NULL?	TYPE
id		INT(7)
NAME		VARCHAR(25)
*/
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;

#3.创建表emp5
/*
NAME	NULL?	TYPEid		
INT(7)First_name	VARCHAR (25)
Last_name	VARCHAR(25)
Dept_id		INT(7)
*/
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
DROP TABLE IF EXISTS emp5;

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

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

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

5.2 常见数据类型介绍

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

*/


#一、整型
/*
分类:
	整数类型      字节	范围
	Tinyint 	1	有符号:-128~127 无符号:0~255
	Smallint	2	有符号:-32768~32767 无符号:0~65535
	mediumint	3	略
	Int、integer	4	略
	Bigint		8	略

特点:
①如果不设置无符号还是有符号,默认是有符号,如果设置无符号,需要添加unsigned关键字
②如果插入的数值超过了整型的范围,会报out of range异常,并且插入临界值
③如果不设置长度,会有默认的长度
 长度代表了显示的最大宽度,如果不够会用0左边填充,必须搭配zerofill使用

*/
#1.如何设置无符号和有符号
USE test;

DROP TABLE IF EXISTS tab_int;
CREATE TABLE tab_int(
	t1 INT(7) ZEROFILL,
	t2 INT(7) UNSIGNED
);
DESC tab_int;

INSERT INTO tab_int VALUES(-123456);
INSERT INTO tab_int VALUES(-12345,12345);

SELECT * FROM tab_int;



#二、小数
/*
分类:
	浮点型:
		float(M,D)   4字节
		double(M,D)  8字节
	定点型:
		DEC(M,D)  M+2字节        最大取值范围与double相同,给定
		DECIMAL(M,D)    M+2字节  decimal的有效取值范围由M和D决定

特点:
①M:整数部位+小数部位
 D:小数部位
 如果超过范围,则插入临界值
 
②M和D都可以省略
 如果是decimal,则M默认为10,D默认为0
 如果是float和double,则会根据插入的数值的精度来决定精度
 
③定点型的精度要求较高,如果要插入数值的精确度要求较高如货币运算时考虑使用

*/

#测试M和D
DROP TABLE IF EXISTS tab_float;
CREATE TABLE tab_float(
	f1 FLOAT(5,2),
	f2 FLOAT(5,2),
	f3 FLOAT(5,2)
);

INSERT INTO tab_float VALUES(123.34,123.34,123.34);
INSERT INTO tab_float VALUES(123.34,123.34,123.345);
INSERT INTO tab_float VALUES(123.3,123.3,123.3);
INSERT INTO tab_float VALUES(1238.3,1238.3,1238.3);

SELECT* FROM tab_float;


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




#三、字符型
/*
较短的文本:
	char(M)	     最多M个字符,可以省略,默认为1     M为0~255之间的整数    
	varchar(M)   最多M个字符,不可以省略            M为0~65535之间的整数  
较长的文本:
	text
	blob

其它:
	binary和varbinary用于保存较短的二进制
	enum用于保存枚举值
	set用于保存集合

特点:
	char(M)	        固定长度的字符    比较耗费空间  效率高
	varchar(M)      可变长度的字符    比较节省空间  效率低
*/

DROP TABLE IF EXISTS tab_char;
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;



DROP TABLE IF EXISTS tab_set;
CREATE TABLE tab_set(
	s1 SET('a','b','c','d')
);

INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('a,b');
INSERT INTO tab_set VALUES('a,b,c');
SELECT * FROM tab_set;




#四、日期型
/*
分类:
	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

特点:
	   字节	     范围	时区等的影响
datetime     8     1000~9999       不受
timestamp    4     1970~2038        受
*/

CREATE TABLE tab_date(
	t1 DATETIME,
	t2 TIMESTAMP
);

INSERT INTO tab_date VALUES(NOW(),NOW());

SET time_zone='+9:00';

SELECT* FROM tab_date;

5.3 常见约束

常见约束
/*

含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性

分类:六大约束
	NOT NULL:非空,用于保证该字段的值不能为空,比如姓名,学号
	DEFAULT:默认,用于保证该字段有默认值,比如性别
	PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空,比如学号,员工编号
	UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空
	CHECK:检查约束【mysql不支持】
	FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值
		     在从表添加外键约束,用于引用主表中某列的值
		     比如学生表的专业标号,员工的部门编号,员工的工种编号

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

约束的添加分类:
	列级约束:
		六大约束语法上都支持,但外键约束没有效果
	表级约束:
		除了非空,默认,其它的都支持
		
主键和唯一的大对比:
		
		保证唯一性	是否允许为空	一个表中可以有多少个	是否允许组合(两个列组合成一个主键)
	主键	    是		      否		最多一个	     是PRIMARY KEY(id,stuName)
	唯一        是	 	      是		可以有多个	     是,不推荐

	组合主键:主键的组合中,全部相同时才代表相同,重复
	
外键:
	1.要求在从表设置外键关系
	2.从表的外键列的类型和主表的关联列的类型要求一致或者兼容,名称无要求
	3.主表的关联列必须是一个key(一般是主键或唯一)
	4.插入数据时,应该先插入从表在插入从表
	  删除数据时,先删除从表,再删除主表
	
*/

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


#一、创建表时添加约束

#1.添加列级约束
/*
语法:
	直接在字段名和类型后面追加 约束类型即可。
	
	只支持:默认、非空、主键、唯一
*/
USE students;

CREATE TABLE 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(majorid)#外键
);

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



#2.添加表级约束
/*

语法:在各个字段的最下面
【constrain 约束名】  约束类型(字段名)
 constrain 约束名:对约束起别名

*/
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(majorid)#外键
);
SHOW INDEX FROM stuinfo;



#通用的写法:
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE IF NOT EXISTS stuinfo(
	id INT ,
	stuName VARCHAR(20),
	sex CHAR(1),
	age INT DEFAULT 18,
	seat INT UNIQUE,
	majorId INT,
	
	PRIMARY KEY(id,stuName),
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorId) REFERENCES major(majorid)
);



#二、修改表时添加约束
/*
1.添加列级约束
  alter table 表名 modify column 字段名 字段类型 新约束;
  
2.添加表级约束
  alter table 表名 ADD 【constrain 约束名】 约束类型(字段名) 【外键的引用】;
*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT ,
	stuName VARCHAR(20),
	sex CHAR(1),
	age INT ,
	seat INT ,
	majorId INT
);
DESC stuinfo;
#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 FOREIGN KEY(majorId) REFERENCES major(majorid);



#三、修改表时删除约束

#1.删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NULL;

#2.删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT;

#3.删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;

#4.删除唯一
ALTER TABLE stuinfo DROP INDEX seat;

#5.删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY majorId;

5.4 标识列

#标识列
/*
又成为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值


特点:
  1.标识列必须和key搭配
  2.一个表中仅仅只能有一个标识列
  3.标识列的类型只能是数值型
  4.标识列可以通过SET auto_increment_increment=3;设置步长
    也可以手动插入值设置起始值:INSERT INTO tab_identity VALUES(10,'john');
*/

#一、创建表时设置标识列

USE test;
CREATE TABLE tab_identity(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20),
	seat INT
);

INSERT INTO tab_identity VALUES(1,'john');
INSERT INTO tab_identity VALUES(NULL,'tom');
INSERT INTO tab_identity(NAME) VALUE('lucy');

SELECT* FROM tab_identity;

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; 

六、TCL(Transaction Control Language)事物控制语言

6.1 事务

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

事务:
  一个或者一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
  事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整
  个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单
  元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执
  行成功,则事物被顺利执行。

存储引擎:
  1、概念:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
  2、通过show engines;来查看mysql支持的存储引擎。
  3、在mysql中用的最多的存储引擎有:innodb,myisam ,memory 等。其中innodb支持事务,而
     myisam、memory等不支持事务
     
事务的ACID(acid)属性(经典面试题)
 1、原子性(Atomicity)
     原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
 2、一致性(Consistency)
     事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
 3、隔离性(Isolation)
     事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据
     对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
 4、持久性(Durability)
     持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作
     和数据库故障不应该对其有任何影响。
     
事物的创建
  1、隐式事物
     没有明显的开始和结束的标志。比如:insert、update、delete语句
  2、显式事物
     具有明显的开始和结束标志。
     前提:必须先设置自动提交功能为禁用
     set autocommit=0;(每次开机都需要设置)
     
     步骤1:开始事务
	set autocommit=0;
	start transaction;可选的
     步骤2:编写事务中的sql语句(select insert update delete)
	语句1
	语句2
	.....
     步骤3:结束事务
	commit;提交事务
	rollback;回滚事务,在提交事务之前回滚可以回到sql语句执行之前的状态
	
	savepoint 节点名; 设置保存
*/

SHOW ENGINES;#查看存储引擎


#创建演示需要用到的数据
DROP TABLE IF EXISTS account;
CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(20),
	balance DOUBLE
);
SET auto_increment_increment=1;#更改插入步长
INSERT INTO account(username,balance)
VALUES('张无忌',1000),('赵敏',1000);

SELECT* FROM account;

#1.演示事务的使用步骤

#开启事务
SET autocommit=0;
START TRANSACTION;
#编写一组事务的语句
UPDATE account SET balance=500 WHERE username='张无忌';
UPDATE account SET balance=1500 WHERE username='赵敏';
#结束事务
ROLLBACK;#在提交之前回滚可以恢复到上面语句执行后之改变前的状态
COMMIT;#提交

SELECT* FROM account;


#2.演示事务对于delete和truncate的处理的区别

SET autocommit=0;
START TRANSACTION;

DELETE FROM account;
ROLLBACK;#回滚对delete删除的数据可以恢复

TRUNCATE TABLE account;
ROLLBACK;#回滚对truncate删除的数据不可以恢复


#3.演示savepoint的使用

SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=1;
SAVEPOINT a;
DELETE FROM account WHERE id=2
SAVEPOINT b;

ROLLBACK TO a;#回滚到保存点a,1删除了,2还没删除


/*
数据的隔离机制:
    对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要
    的隔离机制, 就会导致各种并发问题:
 	1、脏读: 
	   对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 
	   若 T2 回滚, T1读取的内容就是临时且无效的.
	2、不可重复读: 
	   对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再
	   次读取同一个字段, 值就不同了.
 	3、幻读: 
	   对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了
	   一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
 
数据的隔离级别:
    一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔
    离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
    
数据库提供的4种隔离级别:
    READ UNCOMMITTED:读取未提交数据
	允许事务读取未被其他事务提交的变更,脏读、不可重复读、幻读都会出现。
	
    READ COMMITED:读取已提交数据
	只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读仍然可能出现。
	
    REPEATABLE READ:可重复读
	确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事物对这个字段
	进行更新。可以避免脏读和不可重复读,但幻读的情况依然可能出现。
	
    SERIALIZABLE:串行化
	确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更
	新、删除操作。所有并发问题都可以避免,但性能十分低下。
	
Oracle支持的2种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED 
Mysql 支持 4 种事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ

每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 
表示当前的事务隔离级别. 
*/

#查看当前的隔离级别:
SELECT @@tx_isolation;
#设置当前 mySQL 连接的隔离级别: 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
#设置数据库系统的全局的隔离级别:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

6.2 视图

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

应用场景:
    多个地方用到同样的查询结果
    该查询结果使用的sql语句较为复杂
    
	 创建语法关键字	是否实际占用物理空间	      使用	
视图	create view	   没有,只保存了sql逻辑	      增删改查,一般不能增删改
表	create table	占用					   增删改查
*/

#案例:查询姓张的学生名和专业名
USE students;

SELECT studentname,majorname
FROM student s
INNER JOIN major m 
ON s.`majorid`=m.`majorid`
WHERE s.`studentname` LIKE '张%';

#视图可以调用重复使用的部分
CREATE VIEW v1
AS
SELECT studentname,majorname
FROM student s
INNER JOIN major m 
ON s.`majorid`=m.`majorid`;

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


#一、创建视图
/*
语法:
	create view 视图名
	as
	查询语句;
*/
USE myemployees;
#1.查询邮箱中包含a字符的员工名、部门名和工种信息
#①创建
CREATE VIEW myv1
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 j.job_id=e.job_id;

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


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

#②使用
SELECT myv2.ag,g.grade_level
FROM myv2
INNER JOIN job_grades g
ON myv2.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;


#3.查询平均工资最低的部门信息
SELECT* FROM myv2 ORDER BY ag LIMIT 1;

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

SELECT* FROM myv3;



#二、视图的修改

#方式一:
/*
create or replace view 视图名
as
查询语句;
*/
SELECT* FROM myv3;

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


#方式二:
/*
语法:
	alter view 视图名
	as
	查询语句;
*/
ALTER VIEW myv3
AS
SELECT* FROM employees;




#三、删除视图
/*
语法:
	drop view 视图名,视图名。。。。。;
*/
DROP VIEW myv3,myv2;



#四、查看视图

DESC myv3;

SHOW CREATE VIEW myv3;




#五、视图的更新
/*
具备以下特点的视图不允许更新:
	包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all
	常量视图
	Select中包含子查询
	join
	from一个不能更新的视图
	where子句的子查询引用了from子句中的表
*/
CREATE OR REPLACE VIEW myv1
AS
SELECT last_name,email
FROM employees;

SELECT* FROM myv1;
#1.插入
INSERT INTO myv1 VALUES('张飞','zf@qq.com');
#2.修改
UPDATE myv1 SET last_name='张无忌' WHERE last_name='张飞';
#3.删除
DELETE FROM myv1 WHERE last_name='张无忌';

七、存储过程和函数

7.1 存储过程

#存储过程和函数
/*
存储过程和函数:类似于java中的方法

好处:
  1.提高代码复用性
  2.简化操作
*/

#存储过程
/*
含义:一组预先编译好的sql语句,理解成批处理语句
*/

#一、创建语法

CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
	存储过程体(一组合法的SQL语句)
END

注意:
1.参数列表包含三部分
  参数模式  参数名  参数类型
  例如:IN studentname VARCHAR(20)
  
  参数模式:
  IN:该参数可以作为输入,也就是该参数需要调入方法传入值
  OUT:该参数可以作为输出,也就是该参数可以作为返回值
  INOUT:该桉树既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值
  
2.如果存储过程体仅仅只有一句话,BEGIN END 可以省略
  存储过程体中的每条SQL语句的结尾要求必须加分号;
  存储过程体的结尾可以使用DELIMITER重新设置
  语法:
  DELIMITER  结束标记!!!!
  案例:
  DELIMITER $
  
  
#二、调用语法

CALL 存储过程名(实参列表);

#1、空参列表
#案例:插入到admin表中三条记录
SELECT * FROM admin;

DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
	INSERT INTO admin(username,PASSWORD)
	VALUES ('john','0000'),('lily','0000'),('tom','0000');
END $
#调用
CALL myp1()$ 


#2、创建带in模式参数的存储过程

#案例1:创建存储过程,根据女神名,查询对应的男神信息
DELIMITER $
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
	SELECT bo.*
	FROM boys bo
	INNER JOIN beauty be
	ON be.boyfriend_id=bo.id
	WHERE be.name=beautyName;
END $
#调用
CALL myp2('柳岩') $
	
#案例2:创建存储过程实现,用户是否登录成功
DELIMITER $
CREATE PROCEDURE myp3(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:根据女神名返回对应的男神名
DELIMITER $
CREATE PROCEDURE myp4(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
	SELECT bo.boyName INTO boyName
	FROM boys bo
	INNER JOIN beauty be
	ON be.boyfriend_id=bo.id
	WHERE be.name=beautyName;

END $
#调用
CALL myp4('小昭',@bname) $

#案例2:根据女神名,返回对应的男神名和男神魅力值
DELIMITER $
CREATE PROCEDURE myp5(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 be
	ON bo.id=be.boyfriend_id
	WHERE be.name=beautyName;
END
#调用
CALL myp5('小昭',@bname,@userCP) $



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

#案例1:传入a,b两个值,最终a和b翻倍返回
DELIMITER $
CREATE PROCEDURE myp6(INOUT a INT,INOUT b INT)
BEGIN
	SET a=a*2;
	SET b=b*2;
END $
#调用
SET @a=10;
SET @b=20;
CALL myp6(@a,@b) $



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



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

7.2 函数

#函数
/*
含义:一组预先编译好的sql语句,理解成批处理语句

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

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

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

2.函数体:肯定会有return语句,如果没有会报错
	  如果return没有放在函数体的最后也不报错,但不建议
	  return 值
	  	  
3.函数体中仅有一句话,则可以省略begin end

4.使用delimiter语句设置结束标记
  delimiter $
*/

DELIMITER $

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

#----------------------------案例演示--------------------------------
#1.无参有返回
DELIMITER $
CREATE FUNCTION myf1() RETURNS INT
BEGIN
	DECLARE c INT DEFAULT 0;#定义变量
	SELECT COUNT(*) INTO c#赋值
	FROM employees;
	RETURN c;
END $
#调用
SELECT myf1() $


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

#案例2:根据部门名,返回该部门的平均工资
DELIMITER $
CREATE FUNCTION myf3(deptname 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.departmen_id
	WHERE d.department_name=deptname;
	
	RETURN sal;
END $



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


#四、删除函数
DROP FUNCTION myf3;

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

八、流程控制结构

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


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


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

#案例:

#创建储存过程,根据传入的成绩,来显示等级,90-100,显示A,80-90显示B,60-80显示C,否则D
DELIMITER $
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(67)
 


#3.if结构
/*
功能:多重分支
语法:
	if 条件1 then 语句1;
	elseif 条件2 then 语句2;
	....
	elseif 条件n then 语句n;
	else 语句;
	end if;
应用场合:
	应用在begin end中

*/
#创建函数,根据传入的成绩,来显示等级,90-100,显示A,80-90显示B,60-80显示C,否则D
DELIMITER $
CREATE FUNCTION test_if(score INT) RETURNS VARCHAR(1)
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(79)



#二、循环结构
/*
分类:
while、loop、repeat

循环控制:
iterate:类似于continue,继续,结束本次循环,继续下一次
leave:类似于 break,跳出,结束当前所在循环
*/

#1.while:先判断后执行
/*
语法:
	【标签:】while 循环条件 do
		 循环体
	end while 【标签】;

联想:
while(循环条件){
	循环体
}
*/


#2.loop:没有条件的死循环
/*
语法:
	【标签:】loop
		循环体
	end loop 【标签】;
	
可以用来模拟死循环
*/


#3.repeat:先执行,后判断
/*
语法:
	【标签:】repeat
		循环体
	until 结束循环的条件
	end repeat 【标签:】;

*/


#1.没有添加循环控制语句
#案例:批量插入,根据次数插入到admin表中多条记录
DELIMITER $
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	WHILE i<insertCount DO
		INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('rose',i),'0000');
		SET i=i+1;
	END WHILE;
END $
CALL pro_while1(10)
SELECT * FROM admin;


#2.添加leave语句
#案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20停止
DROP PROCEDURE pro_while2;
DELIMITER $
CREATE PROCEDURE pro_while2(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	a:WHILE i<=insertCount DO
		INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('tom',i),'0000');
		IF i>=5 THEN LEAVE a;
		END IF;
		SET i=i+1;
	END WHILE a;
END $
CALL pro_while2(10)
SELECT * FROM admin;


#3.添加iterate语句
#案例:批量插入,根据次数插入到admin表中多条记录,只插入奇数次
DROP PROCEDURE pro_while3;
DELIMITER $
CREATE PROCEDURE pro_while3(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i<=insertCount DO
		SET i=i+1;
		IF i%2=0 THEN ITERATE a;
		END IF;
		INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('jeery',i),'0000');
	END WHILE a;
END $
CALL pro_while3(10)
SELECT * FROM admin;



【练习题】
/*
一、已知表stringcontent
其中字段:
id 自增长
content varchar(20)

向该表插入指定个数的,随机的字符串

*/
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
	id INT PRIMARY KEY AUTO_INCREMENT,
	content VARCHAR(20)
);


DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN 
	DECLARE i INT DEFAULT 1;
	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));#截取长度
		INSERT INTO stringcontent(content) VALUES(SUBSTR(str,startIndex,len));
		SET i=i+1;
	END WHILE;
END $

CALL test_randstr_insert(10)

SELECT * FROM stringcontent;
		
MySQL多数据源是指在一个应用程序中同时使用多个不同的MySQL数据库来存储和管理数据的技术。它可以帮助开发人员更灵活地处理各种数据库操作,提高程序的性能和可扩展性。下面是一个完整的MySQL多数据源教程。 一、设置数据库连接信息 1. 在应用程序的配置件中,创建多个数据库连接的配置项。例如,可以为每个数据源创建一个配置项,分别命名为db1、db2等。 2. 在配置项中,设置每个数据源的连接信息,包括数据库地址、用户名、密码等。 二、创建数据源管理器 1. 创建一个数据源管理器类,用于管理多个数据源。该类需要实现数据源的动态切换和获取。 2. 使用Java的线程安全的数据结构,如ConcurrentHashMap来存储数据源信息。将配置件中的数据库连接信息加载到数据结构中。 3. 实现方法来切换不同的数据源,通过传入数据源的名称来切换到对应的数据库。 三、实现数据源切换 1. 在应用程序中,根据业务需求选择需要使用的数据源。可以通过调用数据源管理器的方法来切换数据源。 2. 在DAO层的代码中,根据当前使用的数据源名称,选择对应的数据源进行数据库操作。 四、使用多数据源进行数据库操作 1. 在DAO层的代码中,区分不同的数据源,并将数据库操作的代码包装在对应的数据源中。 2. 在业务层的代码中,调用DAO层的方法来进行数据库操作。不同的数据源会自动切换。 五、处理事务 1. 如果需要在一个事务中操作多个数据源,可以使用分布式事务的方式来处理。 2. 可以使用开源的分布式事务框架,如Atomikos、Bitronix等来实现多数据源的事务管理。 六、监控和维护 1. 使用监控工具来监控多个数据源的使用情况,包括连接数、查询次数等。 2. 定期对数据库进行维护,包括索引优化、数据清理等工作,以保证数据库的性能和稳定性。 通过以上步骤,我们可以实现MySQL多数据源的配置和使用。使用多数据源可以更好地管理和处理不同的数据库操作,在提高程序性能和可扩展性的同时,也提供了更灵活的数据操作方式。同时,需要注意合理选择和配置数据源,以及监控和维护数据库,以保证系统的运行效率和数据的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值