1.基础概念

1.1 相关概念与常用命令

数据库的好处

1.持久化数据到本地
2.可以实现结构化查询,方便管理

常见概念

1、DB:数据库,保存一组有组织的数据的容器
2、DBMS:数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据
3、SQL: 结构化查询语言,用于和DBMS通信的语言

数据库存储数据的特点

1、将数据放到表中,表再放到库中
2、一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。
3、表具有一些特性,这些特性定义了数据在表中如何存储,类似java中“类”的设计。
4、表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java 中的”属性”
5、表中的数据是按行存储的,每一行类似于java中的“对象”

MySQL的启动和停止

启动:net start 服务名(例如:net start mysql80)

停止:net stop 服务名(例如:net stop mysql80)

MySQL的登录和退出

登录:mysql -h 主机名 -P 端口号 -u 用户名 -p密码 
	(注意:-p和密码中间不能加空格)
	(例如:mysql 【-h localhost P 3306】 -u root -p564445 ,若是登录本地的,【】内的可以不写)

退出:exit 或 ctl+c

MySQL常见命令

show databases #查看所有的数据库
use 库名; #打开指定 的库
show tables ; #显示库中的所有表
show tables from 库名; #显示指定库中的所有表
create table 表名(
	字段名 字段类型,	
	字段名 字段类型
); 创建表

desc 表名; #查看指定表的结构
select * from 表名; #显示表中的所有数据

查询语句

select 字段名 from 表名;
select 字段名1,字段名2...... from 表名;
select * from 表名;
select 常量值;(注意:字符型和日期型的常量值要用单引号括起来,数值型不需要)
select 函数名;
select 表达式;
select 数值+数值; 	/*结果为数值*/
select 字符+数值;	/*试图将字符转换成数值,转换成功则继续运算,转换不成功则把字符当成成0再运算*/
select null+;	/*结果都为null*/

#别名
select 字段名 as 别名 from 表名;
select 字段名 别名 from 表名;	/*可直接用空格代替as*/

#去重
select distinct 字段名 from 表名;

#concat函数
select concat(字符1,字符2...... from 表名;

#ifnull函数
#判断某字段或表达式是否为null,如果为null,返回指定的值(比如指定0),否则返回原本的值
select ifnull(字符, 0) from 表名;

#isnull
#判断某字段或表达式是否为null,是则返回1,不是则返回0
select 字符1 from 表名 where 字符1 is null;
select 字符1 from 表名 where 字符1 is not null;

条件运算符

>
<
>= 
<= 
= ,<=> 安全等于
!= ,<>

逻辑运算符

and(&&):两个条件如果同时成立,结果为true,否则为false
or(||):两个条件只要有一个成立,结果为true,否则为false
not(!):如果条件成立,则not后为false,否则为true

模糊查询

like 
between and
in
is null

排序查询

#语法
select
	要查询的东西
from
	
where 
	条件

order by 排序的字段|表达式|函数|别名 asc|desc
(默认升序)

#实践代码-----------------------------
select * from employees order by salary desc;
select * from employees order by salary asc;

select * 
from employees
where department_id >= 90
order by hiredate asc;

#按年薪的高低显示员工的信息和年薪(按表达式排序)
select *, salary*12*(1+ifnull(commission_pct, 0)) as 年薪
from employees
#order by salary*12*(1+ifnull(commission_pct, 0)) desc;
order by 年薪 desc;

#按姓名长度显示员工的姓名和工资【按函数排序】
select length(last_name) as 字节长度, last_name, salary
from employees
order by 字节长度 desc;

#查询员工信息,先按工资什序,再按员工编号降序【按多个字段排序】
select *
from employees
order by salary asc, employee_id desc;

1.2 实践代码

use myemployees;

select * from employees;
select first_name from employees; 
select first_name as hhh from employees; 
select first_name hhh from employees; 
select distinct department_id from employees; 
select ifnull(commission_pct, 0)  as '奖金率', commission_pct from employees;
select concat(first_name,'___' ,last_name,'___',ifnull(commission_pct, 0)) as output from employees; 
select 100;
select 'xym';
select 100*20 as resulet;
select version();

/*
select 
		查询列表
from
		表名
where
		筛选条件;
*/

select * from employees where salary>12000;
select last_name, department_id from employees where department_id <> 90;

select last_name, salary, commission_pct 
from employees 
where salary >= 10000 and salary <= 20000;

select *
from employees
where not(department_id < 90 or department_id>110) 
	  or salary > 15000;

/*通配符:%任意多个字符
		  _任意单个字符
*/
select * from employees where last_name like '%a%';
select * from employees where last_name like '_e_l';

select * 
from employees 
where last_name 
like '_$_%' escape '$';*/  /*查询第二个字符为_的员工,转义*/

select * 
from employees 
where employee_id 
between 100 and 120;  /*包含临界值(闭区间),不能颠倒大小*/

select last_name, job_id
from employees 
#where job_id='IT_PROT' or  job_id= 'AD_VP' or job_id = 'AD_PRES';
where job_id in('IT_PROT','AD_VP','AD_PRES')

/*
is null:仅可以判断 null
<=>:既可以判断 null,又可以判断数值
*/

select last_name, commission_pct
from employees 
#where commission_pct is null;
where commission_pct is not null;

select last_name, commission_pct
from employees 
where commission_pct <=> null;   /*安全等于*/

select last_name, commission_pct, salary
from employees 
where salary <=> 12000;   /*安全等于*/

2.函数

#语法
select 函数名() from 

2.1 单行函数

字符函数

/*concat拼接
substr截取子串
upper转换成大写
lower转换成小写
trim去前后指定的空格和字符
ltrim去左边空格
rtrim去右边空格
replace替换
lpad左填充
rpad右填充
instr返回子串第一次出现的索引
length 获取字节个数
*/

select length('length');
select upper('select');
select lower('WERTY');
select concat(upper(last_name), lower(first_name)) 姓名 from employees;
select substr('李莫愁神雕侠侣', 4) as aa;	#索引从1开始
select substr('李莫愁神雕侠侣', 4, 2) as aa;	#后面的2表示字符长度
select concat(substr(last_name, 1, 1), '_', lower(substr(last_name, 2))) aa from employees;
select instr('倚天屠龙记屠龙记', '屠龙记') as aa;  #找不到返回0
select length(trim('     子怡     ')) as aa;
select trim('a' from 'aaa子怡aaaa') as aa;
select lpad('你好', 10, '*') output;
select rpad('你好', 10, '*') output;
select replace('张无忌_周芷若_周芷若_周芷若', '周芷若', '赵敏') aa;

数学函数

/*round 四舍五入
rand 随机数
floor向下取整
ceil向上取整
mod取余
truncate截断*/


select round(-1.35);
select round(1.5664, 2);
select ceil(1.564);		#2
select ceil(1);		#1
select floor(1.564);	#1
select floor(1);	#1
select truncate(1.6932, 2);
select mod(10, 3);		#1
select mod(-10, 3);		#-1
select mod(10, -3);  	#1
select mod(-10, -3);	#-1  总结:符号和被除数保持一致

日期函数

/*now当前系统日期+时间
curdate当前系统日期
curtime当前系统时间
datediff(date1, date2)日期之差date1-date2
str_to_date 将字符转换成日期
date_format将日期转换成字符
*/

select now();
select curdate();
select curtime();
select year(now()) , month(now()) ;
select year('1998-8-18') ;
select monthname(now()) ;
select datediff(now(), '1998-10-8');

#str_to_data:将日期格式的字符转换成指定格式日期
select str_to_date('1992-4-3', '%Y-%m-%d') output;
#查询入职日期为1992-4-3的员工信息
select * from employees where hiredate='1992-4-3';
select * from employees where hiredate=str_to_date('4-3-1992', '%m-%d-%Y');

#date-format:将日期转换成字符
select date_format(now(), '%Y年%m月%d日');
#查询有奖金的员工名和入职日期
select last_name, date_format(hiredate, '%m月/%d日 %Y年') 入职日期
from employees
where commission_pct is not null;

其他函数

/*version版本
database当前库
user当前连接用户*/

select version();
select database();
select user();

流程控制函数

/*if 处理双分支
case语句 处理多分支
		情况1:处理等值判断
		情况2:处理条件判断*/
		
select if(10 > 5, '大', '小') aa;
select last_name, commission_pct,  if(commission_pct is null, '没奖金', '有奖金') aa
from employees;

/*
case的使用法一:

case 要判断的字段或表达式
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
when 条件3 then 要显示的值3或语句3
......
else 要显示的值n或语句n
end 【as 别名】
*/
select salary 原始工资, department_id,
case department_id
when 30 then salary*1.1
when 40 then salary*1.2
when 50 then salary*1.3
else salary
end as 新工资
from employees;

/*
case的使用法二:

case 
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
when 条件3 then 要显示的值3或语句3
......
else 要显示的值n或语句n
end 【as 别名】
*/
select salary 原始工资, department_id,
case
when salary>20000 then 'A'
when salary>15000 then 'B'
when salary>10000 then 'C'
else 'D'
end as 工资级别
from employees;

2.2 分组函数

/*sum 求和
max 最大值
min 最小值
avg 平均值
count 计数

特点:
	1、以上五个分组函数都忽略null值且不计入总数,
	2、sum和avg一般用于处理数值型
		max、min、count可以处理任何数据类型
    3、都可以搭配distinct使用,用于统计去重后的结果
	4、count的参数可以支持:字段、*、常量值,一般放1
		count(*),计算行数时null的也会包含进去,效率高,最常用
		count(1),计算行数时null的也会包含进去,效率高
		count(字段名),得到的结果将是除去值为null和重复数据后的结果,效率低
	5、和分组函数一同查询的字段要求是group by后的字段
*/

select sum(salary) from employees;
select max(salary) from employees;
select min(salary) from employees;
select avg(salary) from employees;
select count(salary) from employees;
#组合
select sum(salary), max(salary), round(avg(salary)) from employees;
#查询部门编号为90的员工个数
select count(*) as 个数
from employees
where department_id = 90;

分组查询

/*语法:
	select 查询的字段,分组函数
	from 表
	【where 筛选条件】
	group by 分组的字段
	【order by 子句】
*/
/*
特点:
	1、可以按单个字段分组
	2、和分组函数一同查询的字段最好是分组后的字段
	3、分组筛选
			            针对的表	         位置			    关键字
	      分组前筛选:	原始表		      group by的前面		where
	      分组后筛选:	分组后的结果集	   group by的后面		 having

	4、可以按多个字段分组,字段之间用逗号隔开
	5、可以支持排序
	6、having后可以支持别名
*/


#查询每个工种的最高工资
select max(salary), job_id
from employees
group by job_id;

#查询每个位置的部门个数
select count(*), location_id
from departments
group by location_id;

#查询邮箱中包含a字符的,每个部门的平均工资
select avg(salary), department_id, email
from employees
where email like '%a%'
group by department_id;

#查询有奖金的每个领导手下员工的最高工资
select max(salary), manager_id
from employees
where commission_pct is not null
group by manager_id;

#查询哪个部门的员工个数大于2
select count(*), department_id
from employees
group by department_id
having count(*) > 2;

#查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
select job_id, max(salary)
from employees
where commission_pct is not null
group by job_id
having max(salary) > 12000;

#查询领导编号大于102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
select manager_id, min(salary)
from employees
where manager_id > 102
group by manager_id
having min(salary) > 5000;

#按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
select length(concat(first_name, last_name)) 长度, count(*)
from employees
group by length(concat(first_name, last_name))
having count(*) > 5;

#查询每个部门,每个工种员工的平均工资
select avg(salary), department_id, job_id
from employees
group by department_id, job_id;

#查询每个部门,每个工种员工的平均工资,平均工资大于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.多表连接查询

/*
按功能分类:
	内连接:
		等值连接
		非等值连接
		自连接
	外连接:
		左外连接
		右外连接
		全外连接
	交叉连接
*/

3.1 内连接

/* sql99语法

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

分类:
	等值
	非等值
	自连接
*/

等值连接

#等值连接
#查询员工名和对应的部门名
select last_name, department_name
from employees, departments
where employees.`department_id` = departments.`department_id`;

#为表起别名:提高语句的简洁度,区分重名字段(注意:起了别名后不能用原来的表名)
#查询员工名、工种号、工种名
select last_name, e.job_id, job_title
from employees as e, jobs as j
where e.job_id = j.job_id;

#查询有奖金的员工名、部门名
select last_name, department_name, commission_pct
from employees e, departments d
where e.department_id = d.department_id and e.commission_pct is not null;

#查询城市名中第二个字符为o的部门名和城市名
select department_name, city
from departments d, locations l
where d.location_id = l.location_id and city like '_o%';

#查询每个城市的部门个数
select count(*) 个数, city
from departments d, locations l
where d.location_id = l.location_id
group by city;

#查询每个工种的工种名和员工的个数,并且按员工个数降序
select job_title, count(*)
from employees e, jobs j
where e.job_id = j.job_id
group by job_title
order by count(*) desc;

#三表连接 sql99语法
#查询员工名、部门名、所在城市
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;

#查询员工名、部门名
select last_name, department_name
from employees e
inner join departments d
on e.department_id = d.department_id;

#查询名字中包含e的员工名和工种名
select last_name, job_title
from employees e
inner join jobs j
on e.job_id = j.job_id
where e.last_name like '%e%';

#查询部门个数 > 3 的城市名和部门个数
select city, count(*) 
from departments d
inner join locations l
on l.location_id = d.location_id
group by city
having count(*) > 3;

#查询员工名、部门名、工种名,并按部门名降序
select last_name, department_name, job_title
from employees e
inner join departments d on d.department_id = e.department_id
inner join jobs j on j.job_id = e.job_id
order by department_name desc;

非等值连接

#查询员工的工资和工资级别
select salary, grade_level
from employees e, job_grades j
where salary between j.lowest_sal and j.highest_sal;

#查询员工的工资级别 sql99语法
select salary, grade_level
from employees e
join job_grades g
on e.salary between g.lowest_sal and g.highest_sal;

#查询每个工资级别的个数>20的个数,并且按工资级别降序 sql99语法
select count(*), grade_level
from employees e
join job_grades g
on e.salary between g.lowest_sal and g.highest_sal
group by grade_level
having count(*) > 20
order by grade_level desc;

自连接

#查询员工名和其对应的上级的名称(查员工名要访问一次表,查领导名要再访问一次同一张表)
select e.employee_id, e.last_name, m.manager_id, m.last_name
from employees e, employees m
where m.manager_id = e.employee_id;

#自连接 sql99语法
#查询员工的名字、上级的名字
select e.last_name, m.last_name
from employees e
join employees m
on e.manager_id = m.employee_id;

3.2 外连接

/*
select 查询列表
from 表1 别名
【inner|left outer|right outer|cross】 表2 别名 on 连接条件2
【inner|left outer|right outer|cross】 表3 别名 on 连接条件3
【where 筛选条件】
【group by 分组】
【having 分组后的筛选条件】
【order by 排序列表】

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

/*
应用场景:用于查询一个表中有,另一个表中没有的记录

特点:
	1、外连接的查询结果为主表中的所有记录
		如果从表中有和它匹配的,则显示匹配的值
		如果从表中没有和它匹配的,则显示null
		外连接查询结果 = 内连接结果 + 主表中有而从表中没有的记录
	2、左外连接,left join左边的是主表
	   右外连接,right join右边的是主表
	3、左外和右外交换两个表的顺序,可以实现同样的效果
	4、全外连接 = 内连接的结果 + 表1中有但表2中没有的 + 表2中有但表1中没有的
*/
#引入:查询男朋友不在男神表的女神名
select * from beauty;
select * from boys;
#左外
select b.name, bo.*
from beauty b
left outer join boys bo
on b.boyfriend_id = bo.id
where bo.id is null;
#右外,与上结果同
select b.name, bo.*
from boys bo
right outer join beauty b
on b.boyfriend_id = bo.id
where bo.id is null;
#交叉连接(笛卡尔乘积)
select b.*, bo.*
from beauty b
cross join boys bo;

3.3 子查询

/*
含义:
	一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询,
	在外面的查询语句,称为主查询或外查询

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

where或having后面

/*
一、where或having后面
	1、标量子查询(单行子查询)
	2、列子查询(多行子查询)
	3、行子查询(多行多列)
	
特点:
    1、子查询都放在小括号内
    2、子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧
    3、子查询优先于主查询执行,主查询使用了子查询的执行结果
    4、子查询根据查询结果的行数不同分为以下两类:
        ①单行子查询
            结果集只有一行
            一般搭配单行操作符使用:> < = <> >= <= 
            非法使用子查询的情况:
            	a、子查询的结果为一组值
            	b、子查询的结果为空
        ② 多行子查询
            结果集有多行
            一般搭配多行操作符使用:any、all、in、not in
            in: 属于子查询结果中的任意一个就行
            any和all往往可以用其他查询代替 
*/


#1、标量子查询(单行子查询)------------------------------------------------
#①查询Abel的工资
select salary
from employees
where last_name = 'Abel';
#查询员工的信息,满足 salary>①结果
select *
from employees
where salary > (
	select salary
	from employees
	where last_name = 'Abel'
);

#②返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
#第一步:①查询141号员工的job_id
select job_id
from employees
where employee_id = 141;
#第二步:②查询143号员工的salary
select salary
from employees
where employee_id = 143;
#第三步:查询员工的姓名,job_id和工资,要求job_id=①并且salary>②
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
);

#查询最低工资大于50号部门最低工资的部门id和其最低工资
select min(salary), department_id
from employees
group by department_id
having min(salary) > (
	select min(salary)
    from employees
    where department_id = 50
);



#2、列子查询(多行子查询)------------------------------------------------
#返回location_id是1400或1700的部门中的所有员工姓名
select last_name
from employees
where department_id in(
	select distinct department_id
    from departments
    where location_id in(1400, 1700)
);
#或
select last_name
from employees
where department_id =any(
	select distinct department_id
    from departments
    where location_id in(1400, 1700)
);

#返回location_id不是1400或1700的部门中的所有员工姓名
select last_name
from employees
where department_id not in(
	select distinct department_id
    from departments
    where location_id in(1400, 1700)
);
#或
select last_name
from employees
where department_id <>all(
	select distinct department_id
    from departments
    where location_id in(1400, 1700)
);


#返回其他部门(意思是除去部门IT_PROG的)中比job_id为'IT_PROG'工种任一工资低的员工的员工号、姓名、job_id以及salary
#一、先查询job_id为'IT_PROG'工种任一工资
select distinct salary
from employees
where job_id = 'IT_PROG';
#二、查询员工号、姓名、job_id以及salary,salary < (一)的任意一个
select last_name, employee_id, job_id, salary
from employees
where salary < any(
	select distinct salary
	from employees
	where job_id = 'IT_PROG'
) and job_id <> 'IT_PROG';
#或
select last_name, employee_id, job_id, salary
from employees
where salary < (
	select  max(salary)
	from employees
	where job_id = 'IT_PROG'
) and job_id <> 'IT_PROG';

#返回其他部门(意思是除去部门IT_PROG的)中比job_id为'IT_PROG'工种所有工资低的员工的员工号、姓名、job_id以及salary
select last_name, employee_id, job_id, salary
from employees
where salary < all(
	select distinct salary
	from employees
	where job_id = 'IT_PROG'
) and job_id <> 'IT_PROG';
#或
select last_name, employee_id, 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, salary) = (
	select min(employee_id), max(salary)
    from employees
)select后面

from后面

#查询每个部门的平均工资的工资等级
#(将子查询结果充当一张表,要求必须其别名)
select ag_dep.*, g.grade_level
from (
	select avg(salary) ag, department_id
	from employees
	group by department_id
) ag_dep
inner join job_grades g
on ag_dep.ag between lowest_sal and highest_sal;

exists后面

#结果为0或1,判断是否存在
select exists(
	select employee_id
    from employees
    where salary=300000
);

#查询有员工的部门名
select department_name
from departments d
where exists(
	select *
    from employees e
    where d.department_id = e.department_id
);
#或
select department_name
from departments d
where d.department_id in(
	select department_id
    from employees
);

#查询没有女朋友的男神信息
use girls;
select bo.*
from boys bo
where bo.`id` not in(
	select boyfriend_id
    from beauty
);
#或
select bo.*
from boys bo
where not exists(
	select boyfriend_id
    from beauty b
    where bo.`id`=b.`boyfriend_id`
);

3.4 分页查询

/*语法:
select 字段|表达式,...
from 表
【where 条件】
【group by 分组字段】
【having 条件】
【order by 排序的字段】
limit 【offset起始的条目索引,】 size条目数;

特点:
	1.起始条目索引从0开始
	2.limit子句放在查询语句的最后
	3.公式:select * from  表 limit (page-1)*sizePerPage,sizePerPage
            假如:
            每页显示条目数sizePerPage
            要显示的页数 page
*/
#查询前五条员工信息
select * from employees limit 0, 5;

#查询第11条到第25条
select * from employees limit 10, 15;

#有奖金的员工信息,并且工资较高的前10名显示出来
select *
from employees
where commission_pct is not null
order by salary desc
limit 10;

3.5 联合查询

/*
语法:
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union  【all】
.....
select 字段|常量|表达式|函数 【from 表】 【where 条件】

特点:
	1、多条查询语句的查询的列数必须是一致的
	2、多条查询语句的查询的列的类型和顺序要一致
	3、union代表去重,union all代表不去重
*/

#查询部门编号>90或者邮箱包含a的员工信息
select *
from employees where email like '%a%' or department_id > 90;
#等同于
select * from employees where email like '%a%'
union
select * from employees where department_id > 90;

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

4.SQL语言

4.1 增删改

插入

/*
语法:
	方式一:(支持插入多行,子查询)
		insert into 表名(字段名,...)
		values(值1,...), (值1,...), ......;
	方式二:(不支持插入多行,不支持子查询)
		insert into 表名
		set 列名=值, 列名=值, ......;

特点:
    1、字段类型和值类型一致或兼容,而且一一对应
    2、可以为空的字段,可以不用插入值,或用null填充
    3、不可以为空的字段,必须插入值
    4、字段个数和值的个数必须一致
    5、字段可以省略,但默认所有字段,并且顺序和表中的存储顺序一致
*/

#方式一:多行
insert into beauty(id, name, sex, borndate)
values(13, '唐艺昕', '女', '1990-4-3'), (14, '刘亦菲', '女', '1987-4-3');

#方式一:子查询(会把结果插入对应的字段中)
insert into beauty(id, name, phone)
select 26, '宋茜', '88888888888'

#方式二:
insert into beauty
set id=13, name='唐嫣', sex='女', borndate='1998-9-1';

删除

/*
单表的删除:
	delete from 表名 【where 筛选条件】

多表的删除:
	delete 别名1,别名2
	from 表1 别名1,表2 别名2
	where 连接条件
	and 筛选条件;

	truncate语句:(又称清空,一次删除整个表,不能加where条件)
		truncate table 表名
		
区别:
	1.truncate不能加where条件,而delete可以加where条件
	2.truncate的效率高一丢丢
	3.truncate 删除带自增长的列的表后,如果再插入数据,数据从1开始delete 删除带自增长列的表后,如果再插入数据,数据从上一次的断点处开始
	4.truncate删除不能回滚,delete删除可以回滚
*/

#删除手机号以9结尾的女神信息
delete from beauty where phone like '%9';

#删除张无忌女朋友的信息
delete b
from beauty b
inner join boys bo
on b.boyfriend_id = bo.id
where bo.boyName = '张无忌';

#删除黄晓明的信息和他女朋友的信息
delete b, bo
from beauty b
inner join boys bo
on b.boyfriend_id = bo.id
where bo.boyName = '黄晓明';

修改

/*
修改单表语法:
	update 表名 
	set 字段=新值,字段=新值
	【where 条件】
	
修改多表语法:
	update 表1 别名1,表2 别名2
	set 字段=新值,字段=新值
	where 连接条件
	and 筛选条件
*/

#修改beauty表中姓唐的女神的电话为88888888888, 
update beauty
set phone='88888888888'
where name like '唐%';

#修改张无忌女朋友的手机号为123
update boys bo
inner join beauty b
on bo.id = b.boyfriend_id
where bo.boyName='张无忌';

4.2 库表管理

库的管理

#库的管理
    #一、创建库
    create database (if not exists) 库名;
    #二、删除库
    drop database (if exists) 库名;
    #三、库的修改
    alter database 旧库名 to 新库名;

表的管理

#表的管理
	#一、表的创建
        create table 表名(
            列名 列的类型 (长度)约束】,
            列名 列的类型 (长度)约束】,
            列名 列的类型 (长度)约束】,
            ...
        );
        #例如:
        CREATE TABLE IF NOT EXISTS stuinfo(
            stuId INT,
            stuName VARCHAR(20),
            gender CHAR,
            bornDate DATETIME
        );
	#二、表的修改
		#1、修改列名
			alter table 表名 change column 旧列名 新列名 类型;
			#例如:
			alter table book change column publishdate pubdate datetime;
		#2、修改列的类型或约束
			alter table 表名 modify column 列名 类型;
			#例如:
			alter table book modify column pubdate timestamp;
		#3、添加新列
			alter table 表名 add column 列名 类型;
			#例如:
			alter table author add column annual double;
		#4、删除列
			alter table 表名 drop column 列名 类型;
			#例如:
			alter table author drop column annual double;
		#5、修改表名
			alter table 旧表名 rename to 新表名;
			#例如:
			alter table author rename to zuozhe;
	#三、表的删除
		drop table if exists 表名;
	#四、表的复制
		#1、只复制表的结构,不复制数据
			create table 新表名 like 被复制表名;
			例如:
			create table copy_author like author;
		#2、复制表的结构 + 全部数据
			create table 新表名 select * from 被复制表名;
			例如:
			create table copy_author select * from author;
		#3、复制表的结构 + 部分数据
			create table 新表名
			select 列名, 列名, ...
			from 被复制表名
			where 筛选条件】 ;
		#4、只复制部分结构
			create table 新表名
			select 列名, 列名, ...
			from 被复制表名
			where 0;

4.3 数据类型

数值类型

1、整数
	特点:
		a.默认是有符号,无符号需添加unsigned关键字
		b.若插入的数值超出范围,会报out of range异常, 并且插入临界值
		c.如果不设置长度,会有默认长度,长度代表了显示的最大宽度,如果不够会用0在左边填充,但必须搭配zerofill使用
2、小数
	分类:
		a.浮点型
			float(M, D)
			double(M, D)
		b.定点型
			dec(M, D)
			decimal(M, D)
	特点:
		M:整数部位长度+小数部位长度
		D:小数部位长度
	注意:M和D都可以省略,如果是decimal,则M默认是10, D默认是0
		 			   如果是float和double,则会根据插入的数值精度来决定精度
	总结:定点型的精度较高,如果要求插入数值的精度较高如货币运算等可以考虑使用

字符串类型

/*
1、较短的文本(M为字符数)
	char(M)       固定长度的字符    空间耗费大,但效率高
	varchar(M)	  可变长度的字符    空间耗费小,但效率低
2、较长的文本
	text
	blob
*/

#--------------------------------------------------------------------
enum类型#又称枚举类型,要求插入的值必须属于列表中指定的值之一

create table tab_char(
	c 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('A');
insert into tab_char values('m');  #报错,因为不包含在枚举列表中

#---------------------------------------------------------------------
set类型 #和enum类型类似,区别在于set类型一次可以选取多个成员,而enum只能选取一个成员

create table tab_set(
	s 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');

4.4 常见约束

创建表时添加约束

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

创建表时添加约束

/*
常见六大约束:
    NOT NULL			保证字段不为空
    DEFAULT				保证字段有默认值
    UNIQUE				唯一,保证字段值具有唯一性,可以为空
    CHECK				检查约束(mysql不支持)
    PRIMARY KEY		    主键,保证字段值具有唯一性,不能为空
    FOREIGN KEY			外键,用于限制两个表的关系,保证该字段的值必须来自于主表的关联列的值,在从表中添加外键约束,用于引用主表中某列的值
*/

#一、添加列级约束----------------------------
/*	语法:
		直接在字段名和类型后面追加约束类型即可
		只支持:默认、非空、主键、唯一
*/
		
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,		#默认约束
    majorId int references(id)		#外键
);

#二、添加表级约束----------------------------
/*
语法:在各个字段的最下面
	【constraint 约束名】 约束类型(字段名);
*/

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) #外键
);

#三、通用写法----------------------------
create table if not exists stuinfo(
	id int primary key,
    stuName varchar(20) not null,
    sex char(1),
    seat int unique 18,
    age int default,
    majorId int,
    constraint fk_stuinfo_major foreign key(majorid) references major(id)
);

/*
主键和唯一的对比:
		唯一性		是否允许为空		一个表中可以有多少个		是否允许组合
主键		√			×				一个					允许(不推荐)
唯一		√			√				多个					允许(不推荐)

外键:
	1、要求在从表设置外键关系
	2、从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
	3、主表的关联列必须是一个key(一般是主键或唯一)
	4、插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表

*/

修改表时添加约束

/*
1、添加列级约束
	alter table 表名 modify column 字段名 字段类型 新约束;
2、添加表级约束
	alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
*/


alter table stuinfo modify column stuname varchar(20) not null;

alter table stuinfo modify column age int default 18;

alter table stuinfo modify column id int primary key;
alter table stuinfo add primary key(id);

alter table stuinfo modify column seat int unique;
alter table stuinfo add unique(seat);

alter table stuinfo add foreign key(majorid) references major(id);

修改表时删除约束

alter table stuinfo modify column stuname varchar(20) null;

alter table stuinfo modify column age int;

alter table stuinfo drop primary key;

alter table stuinfo drop index seat;

alter table stuinfo drop foreign key fk_stuinfo_major;

标识列

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

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

#一、创建表时设置标识列
create table tab_identity(
	id int primary key auto_increment,
    name varchar(20)
);
#会自动创建1-20id
insert into tab_identity(id, name) values(null, 'john');

#二、修改表时设置标识列
alter table tab_identity modify column id int primary key auto_increment;

#三、修改表时删除标识列
alter table tab_identity modify column id int;

4.5 事务控制

/*
transaction control language:事务控制语言
事务:
	一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行
*/
/*
事务的特性:
	原子性:一个事务不可再分割,要么都执行要么都不执行
	一致性:一个事务执行会使数据从一个一致状态切换到另外一个一直状态
	隔离性:一个事务的执行不受其他事务的干扰
	持久性:一个事务一旦提交,则会永久的改变数据库的数据

事务的创建:
	隐式事务:事务没有明显的开启或结束的标记,比如insert、update、delete语句
	显示事务:事务具有明显的开启或结束的标记,前提:必须先设置自动提交功能为禁用
			set autocommit=0;
			
			步骤1:开启事务
				set autommit=0;
				start transaction;(可选)
			步骤2:编写事务中的sql语句(select、insert、update、delete)
				语句1;
				语句2;
				...
			步骤3:结束事务
				commit; (提交事务)
				rollback; (回滚事务)
*/

set autocommit=0;
start transaction;
update account set balance = 500 where username='张无忌';
update account set balance = 1500 where username='赵敏';
commit;


/*
事务并发问题如何发生?
	当多个事务同时操作同一个数据库的相同数据时
	
事务的并发问题有哪些?
	脏读:一个事务读取到了另外一个事务未提交的数据
	不可重复读:同一个事务中,多次读取到的数据不一致
	幻读:一个事务读取数据时,另一个事务进行更新,导致第一个事务读取到了没有更新的数据

如何避免事务的并发问题?
	通过设置事务的隔离级别
	1、read uncommitted
	2、read committed 可以避免脏读
	3、repeatable read 可以避免脏读、不可重复读和一部分幻读
	4、serializable 可以避免脏读、不可重复读和幻读

设置隔离级别:
	set session|global  transaction isolation level 隔离级别名;

查看隔离级别:
	select @@tx_isolation;
*/

4.6 视图

#视图
/*
含义:
	虚拟表,和普通表一样使用
好处:
	1、sql语句提高重用性,效率高
	2、和表实现了分离,提高了安全性
视图和表的区别:
			  使用方式	   占用物理空间				语法

		视图	 完全相同	不占用,仅仅保存的是sql逻辑    create view
		表	  完全相同	 占用						  create table
*/
一、视图的创建
#查询姓名中包含a字符的员工名、部门名和工种信息
1、创建
create view myview
as
select last_name, department_name, job_title
from employees e
join departments d on e.department_id = d.department_id
join jobs j on e.job_id = j.job_id;
2、使用
select * from myview where last_name like '%a%';

#查询平均工资最低的部门名和工资
create view myv2
as
select avg(salary) ag
from employees
group by department_id;

create view myv3
as
select * from myv2
order by ag
limit 1;

select d.*, m.ag
from myv3 m
join departments d
on m.department_id = d.department_id;

二、视图的修改
/*
	方式一:
        create or replace view 视图名
        as
        查询语句
*/
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 myv1, myv2, myv3;

四、视图的查看
/*
	语法:
		show create view 视图名;
*/

五、视图的更新(更改数据)
	1、插入
		insert into myv1 values('张飞', 'zf@qq.com');
	2、修改
		update myv1 set last_name = '张无忌' where last_name = '张飞';
	3、删除
		delete from myv1 where last_name = '张无忌';

4.7 变量

/*
系统变量:
	全局变量:服务器每次启动将为所有的全局变量赋初值,针对于所有的会话有效,但不能			    跨重启
	会话变量:仅仅针对当前会话有效
自定义变量:
	用户变量:针对于当前会话有效,应用在任何地方,即begin end里面或begin end外面
	局部变量:仅仅在它的begin end中有效,应用在begin end中的第一句话
*/
一、系统变量
说明:由系统提供,属于服务器层面
1、查询所有的系统变量
show global|【session】 variables;
2、查看满足条件的部分系统变量
show global|【session】 variables like '%char%';
3、查看指定的某个系统变量的值
select @@global|【session】.系统变量名;
4、为某个系统变量赋值
set global|【session】 系统变量名=值;
set @@global|【session】.系统变量名=值;


二、自定义变量
1、说明:用户自定义变量
    使用步骤:
    声明->赋值->使用
    1、声明并初始化
    set @用户变量名=值; 
    set@用户变量名:=值; 
    select @用户变量名:=值;

    2、赋值(更新用户变量的值)
    方式一:
    set @用户变量名=值; 
    set@用户变量名:=值; 
    select @用户变量名:=值;
    方式二:
    select 字段 into 变量名
    from 表;

    3、使用
    select @用户变量名;

    例如:
        set @name='john';
        set @name=100;
        set @count=1;

        select count(*) into @count
        from employees;

        select @count;
    
2、局部变量:
	1、声明
		declare 变量名 类型;
		declare 变量名 类型 default 值;
	2、赋值
		方式一:通过 set 或 select
			set 局部变量名=值;
			set 局部变量名:=值;
			select @局部变量名:=值;
		方式二:通过 select into
			select 字段 into 局部变量名
			from 表;
	3、使用
		select 局部变量名;
		
	例如:
		declare m int default 1;
		declare n int default 2;
		declare sum int;
		set sum=m+n;
		select sum;

4.8 存储过程和函数

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

一、创建语法
create procedure 存储过程名(参数列表)
begin
	存储过程体(一组合法的SQL语句)
end
注意:
1、参数列表包含三部分
    参数模式 参数名 参数类型

    例如: in stuname varchar(20)

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

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

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


三、参数前面的符号的意思
in:该参数只能作为输入 (该参数不能做返回值)
out:该参数只能作为输出(该参数只能做返回值)
inout:既能做输入又能做输出

5.流程控制

5.1 函数

/*
含义:一组预先编译好的SQL语句的集合,可理解成批处理语句
	1、提高代码的重用性
	2、简化操作
	3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
	
区别:
	存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新
	函数:有且仅有一个返回,适合做处理数据后返回一个结果
*/

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

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

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

三、查看函数
show create function 函数名;

四、删除函数
drop function 函数名;
*/

#返回公司的员工个数(无参返回)
create function myf1() returns int
begin
	declare c int default 0	#定义变量
	select count(*) into c	#赋值
	from employees;
	return c;
end $

#根据员工名返回他的工资(有参返回)
create function myf2(cmpName varchar(20)) return double
begin
	set @sal=0;
	select salary into @sal
	from employees
	where last_name = empName;
	return @sal;
end $

5.2 流程控制图

/*
顺序结构:程序从上往下依次执行
分支结构:程序从两条或多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码
*/
一、分支结构
1、 if 函数
	语法:if(条件,值1,值2)
	特点:可以用在任何位置
2、 case 结构
	情况一:类似于switch
    case 表达式
    when 值1 then 结果1或语句1(如果是语句,需要加分号) 
    when 值2 then 结果2或语句2(如果是语句,需要加分号)
    ...
    else 结果n或语句n(如果是语句,需要加分号)
    end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)

    情况二:类似于多重if
    case 
    when 条件1 then 结果1或语句1(如果是语句,需要加分号) 
    when 条件2 then 结果2或语句2(如果是语句,需要加分号)
    ...
    else 结果n或语句n(如果是语句,需要加分号)
    end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)
3、 if 结构
    if 情况1 then 语句1;
    elseif 情况2 then 语句2;
    ...
    else 语句n;
    end if;
    
二、循环结构
语法:
	【标签:】WHILE 循环条件  DO
        循环体
    END WHILE 【标签】;
    
特点:
    只能放在BEGIN END里面
    如果要搭配leave跳转语句,需要使用标签,否则可以不用标签
    leave类似于java中的break语句,跳出所在循环!!!