// Fork me on GitHub

mysql最全查询语句

基本查询语句及语法:

  • select
  • distinct
  • from
  • where
  • group by
  • having
  • limit

tip:1.起别名的两种方式 AS 和 空格,别名最好加引号,单双都可

   2.select '123'+90;只要其中一方为字符型,试图将字符型转换为数值型

              如果转换成功,则继续做加法运算

   select 'john'+90;    如果转换失败,则将字符型数值转换成0

   select null+90;       只要其中有一方为null,则结果肯定为null;

    select concat(last_name,first_name)  as 姓名 from employees; concat函数对查询结果的拼接

  3.select ifnull(exp1,expe) as 结果; 如果exp1的值为null,则查询结果为exp2 ,别名为结果;

示例:

SELECT CONCAT(first_name,last_name,job_id,IFNULL(commission_pct,0))

FROM employees;

分类:

  一、按条件表达式筛选

    简单运算符:> < = != <> >= <=   tip:<> 就是不等于

  二、按逻辑表达式筛选

    逻辑运算符: && || !

          and or not

    作用:用于连接条件表达式

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

  三、模糊查询

    like / between and / in / is null|is not null

 

1.like

#查询员工中包含字符a的员工信息
SELECT * FROM employees WHERE last_name LIKE '%a%';

#查询员工名字中第二个字符为_的员工名,需要转义

select * from employees where last_name like '_\_%';
或者 ...like '_$_%' escape '$';

2.between and 包含区间两端
select *from employees where employe_id between 100 and 120;


3.in(括号里不能用通配符)

含义:判断某字段的值是否属于in列表中的某一项,要求括号中的值必须一致或兼容

#查询员工的工种编号是IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号

SELECT last_name,job_id employees WHERE job_id='' OR job id =''OR job_id='';

#这里需要注意IT_PROG等是varchar类型,在查询时需要加上双引号

SELECT last_name,job_id FROM employees WHERE job_id IN ('IT_PROG','AD_VP','AD_PRES');
4.isnull(=号表示判断,=号不能判断null值,)

#查询没有奖金的员工名和奖金率

SELECT last_name,commission_pct FROM employees WHERE commission_pct IS NULL;

补充:安全等于<=> ,可以判断null值 ,可以理解为is和=的合体,尽量避免使用!=



五、经典面试题

试问:select * from employees;和
select * from employees where commission_pct like '%%' and last_name like '%%';
结果是否一样?

不一样
如果判断的字段有null值,下面一句就不包含 有null的结果

常见函数:

分类:

1、单行函数,如concat、length、ifnull等

字符函数
1.length(),获取参数值字节个数
show variables like '%char%',显示字符集

2.concat(),拼接字符串

3.upper()/lower()
select concat(UPPER(last_name),Lower(first_name)) 姓名 from employees;

4.substr、substring 截取字符串,闭区间
#截取从指定索引处后面所有字符
select suberstr('李莫愁爱上了陆展元',7) out_put;#返回陆展元,
tip:sql中索引都是从1开始的;
#截取从指定索引处指定长度的字符
select substr('李莫愁爱上了陆展元',1,3) out_put;#返回
李莫愁
案例:姓名中首字符大写,其他字符小写,然后用'_'拼接,显示出来
select concat(upper(substr(last_name,1,1)),'_',lower(substr(last_name,2))) output from employees;

5.instr(),返回子串第一次出现的索引,如果找不到返回0;
select instr('杨不悔爱上了殷六侠','殷六侠') as output ;,返回6

6.trim 去掉前后的字符
select trim(' 张翠山 ') as out_put; #张翠山
下面注意使用from
select trim('a'from'aaaaaa张aaaaaa翠aaaaa山aaa') as output; #张
aaaaaaaaaaa

7.lpad用指定的字符实现左填充指定长度
select LPAD('殷素素',2,'*') as output;如果指定长度较小会截断 #殷素

8.rpad同上

9.replace 替换
select replace('张无忌爱上了周芷若
周芷若周芷若周芷若',周芷若','赵敏') as output;
全部替换


数学函数
1.round 四舍五入
select round(-1.55);-2
select round(1.567,2);1.57
2.ceil向上取整,返回>=该参数的最小整数
select ceil(1.002);2
3.floor向下取整

4.truncate 截断
select truncate(1.65999,1)#1.6

5.mod 取余 符号与被除数一致 可按公式a-a/b*b来理解;
日期函数
1.now()返回当前系统日期+时间
select now();
2.curdate() 返回当前系统日期,不包含日期
select curdate();
3.curtime()
可以获取指定的部分,年、月、日、小时、分钟、秒
select year(now()) 年;
select year('1998-1-1') 年;#1998

select month(now());or select monthname(now());

4.str_to_date:将日期格式的字符串转换成指定格式的日期,解析字符串
str_to_date('9-13-1999','%m-%d-%y') 1999-09-13

date_format:将日期转换成字符
date_format('2018/6/6','%y年%m月%d日') 2018年06月06日

 


 示例:查询入职日期为1992--4-3的员工信息

select * from employees where hiredate='1992-4-3';

如果客户提供的字符串为4-3 1993'

select * from employees where hiredate=str_to_date('4-3 1992','%c-%d %Y');

示例2:
select date_format(now(),'%y年%月%d日')as output;
#查询有奖金的员工名和入职日期(xx月/xx日 xx年)
select last_name,date_format(hiredate,'%m月/%d日 %y年')入职日期
from employees where commission_pct is not null;



其他函数

select version();
select databases();
select user();
流程控制函数
1.if函数:
select if (10>5,'大','小');类似三元运算符
select last_name,commission_pct,if(commission_pct is null,'没奖金,呵呵','有奖金') 备注 from employees;

2.case函数的使用一: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 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函数的使用二: 类似于多重if
case
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
。。。
else 要显示的值n或语句n
end

#案例:查询员工的工资的情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资》10000,显示C级别
否则,显示D级别

SELECT salary ,
CASE
WHEN salary>20000 THEN 'a'
WHEN salary>15000 THEN 'b'

when salary>10000 then 'c'

ELSE 'd'
END AS '级别' FROM employees;


select concate(last_name,'earn',salary,'nonthly but wangts',salary*3)as 'dream salary'
from employees where salary = 24000;


 

2、分组函数,又称统计函数,聚合函数

sumavgmaxmincount
#1简单使用
select sum(salary) from employees;

#2参数支持哪些类型
select max(last_name),min(last_name) from employees
select max(hiredate),min(hiredate) from employees;
#count支持除了null的任何类型
select count()

#3是否忽略null
以上的分组函数都忽略null值

#4和distinct搭配
select count(distinct) from employees;

#5count函数的详细介绍
select count(*) from employees;#统计不全为null的行数

select count(1) from employees;#count()参数写个常量,等于是加了一列的常量
select count('ss').

#6和分组函数一同查询的字段要求是groupby后面的字段




 

练习:
查询员工表中最大入职时间和最小入职时间的相差天数

select datadiff(max(hiredate),min(hiredate)) difference from employees;


查询部门编号为90的员工个数
select count*)个数 from employees where department_id=90

group by  

tip(注意关键词 ‘每个’)

1.查询每个工种的最高工资

SELECT MAX(salary),job_id FROM employees  GROUP BY job_id ; 

2.查询每个位置上的部门个数

select count(*),location_id from departments group by location_id ;

3.添加筛选条件

#查询邮箱中包含a字符的,每个部门的平均工资
SELECT   AVG(salary) ,department_id    FROM employees WHERE email LIKE '%e%' 

GROUP BY department_id


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

#查询哪个部门的员工个数大于2
常规路子:
第1步:查询每个部门的员工个数
第2步:根据1的结果进行筛选,查询哪个部门的员工个数>2
having:
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;

tip:group by / having/oder by 后面支持别名的,但是where不支持


#按多个字段进行分组
#案例:查询每个部门每个工种的员工的平均工资
select avg(salary),department_id;job_id from employees group by
departments_id,job_id;没有顺序要求

#添加排序
select avg(salary),department_id;job_id from employees group by 
departments_id,job_id order by avg(salary) desc;
 

进阶6:多表查询

分类:

按年代分类:

sql192标准:仅支持内连接 

sql199标准:支持内连接+外连接(左外和右外)+交叉连接,也是mysql中所支持的

tip:1多表等值连接的结果为多表的交集部分

   2 给、n个表连接至少需要n-1个连接条件

     3多表的顺序没有要求

     4一般需要为表起别名

     5可以搭配排序、分组、筛选等语句使用

按功能分类:

内连接:等值连接,非等值连接,自连接

外连接:左外连接,右外连接,全外连接

交叉连接

 

一、sql92语法

#1、等值连接
案例:查询女神和对应的男生名
select name,boyname from boys,beauty where beauty.boyfriend_id=boys.id;


#2、为表起别名
查询员工名、工种号、工种名
select last_name,employees.job_id,job_title from employees,jobs where 
employees.job_id = jobs.job_id; 上一句太繁琐 select last_name,e.job_id,j.job_title from employees as e,jobs as j where
e.job_id = j.job_id;

#3加筛选
案例1:查询有奖金的员工名、部门名
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;


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

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

案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
select department_name,d.manager_id,min(salary) from deparments d,employees e
where d.department_id =e.department_id and e.commission_pct id not null
group by d.department_id,d.manager_id;
易错:需分两组,因为不能保证department_id和manager_id是一一对应关系;
注意区别d.manager_id和e.manager_id

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

#6三表连接
案例:查询员工名、部门名和所在城市
select last_name,department_name,city from employees e, departments d ,locations l where
d.department_id=e.department_id and d.location_id = l.location_id;

 

 

 

 

#2非等值连接

案例1:查询员工的工资和工资级别
select salary,grade_level from employees e,job_grades j where 
j.lowest.sal <=e.salary<j.highest.sal;
或者 where salary betwwen j.lowest_sal and j.highest_sal;


#3自连接

#案例:查询 员工名和上级的名称
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语法

内连接,inner

外连接(左外 left(outer),右外 right,全外full),

交叉连接  cross

语法:

slect 查询列表

from 表1 别名 【连接类型】

join 表2 别名

on 连接条件

where 筛选条件

group by 分组

having 筛选条件

order 排序列表

特点:inner可以省略,

筛选条件放在where后面,连接条件放在on后面,提高分离性,可读

inner join 连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集

 

 

内连接
分类:等值连接、非等值、自连接

#(一)等值连接
案例1:
select last_name,department_name from employees e inner join 
departments d on e.department_id = d.department_oid;

案例2、查询名字中包含e的员工名和工种名(添加筛选)
select last_name,job_title from employees e inner join jobs j on e.job_id=j.job_id
where e.last_name like '%e%';

案例3、查询部门个数>3的城市名和部门个数(分组加筛选)

SELECT city,COUNT(*) 部门个数 FROM departments d INNER JOIN locations l ON
d.location_id=l.location_id GROUP BY city HAVING COUNT(*)>3;

案例4:查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)

select department_name,count(*) from employees e inner join departments d on

e.department_id =d.department_id group by d.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 department_name desc;

 #(二)非等值连接

#查询员工的工资级别

select salary,grade_level from employees e inner join job_grade g on e.salary between g.lowest_sal and g.highest_sal;

#查询每个工资级别的个数,并且按工资级别降序

SELECT grade_level,COUNT(*) FROM employees e INNER JOIN job_grades j
ON e.salary BETWEEN lowest_sal AND highest_sal GROUP BY grade_level
ORDER BY grade_level DESC;

#(三)自连接

#查询员工的名字、上级的名字

select e.last_name,m.last_name from employees e join employees m 

on e.manager_id = m.employee_id;

 

 

二、外连接
应用场景:用于查询一个表中有,一个表中没有的情形

特点:1、外连接的查询结果为主表中的所有记录
        如果从表中有和它匹配的,则显示匹配值
        如果从表中没有和它匹配的,则显示null值;
        外连接的查询结果=内连接的结果+主表中有而从表中没有的记录
2、左外连接,left join左边的是主表
    右外连接,right join右边的是主表
3、左外和右外交换两个表的顺序,可以实现通用
4、全外连接= 内连接+表1中有但表2中没有的+表2中有但表1中没有的 #引入 :查询男朋友不在男神表的女神名
左外
SELECT b.name,bo.* FROM beauty b LEFT OUTER JOIN boys bo ON b.boyfriend_id = bo.id WHERE bo.id IS NULL; #选择主键

案例1:查询哪个部门没有员工
左外
select d.*,employee_id from departments d left outer join employees e on d.department_id =
e.department_id where employee_id is null;

#全外
select b,*,bo.* from beauty b full outer join boys bo on b.boyfriend_id = bo.id;

#交叉连接 笛卡尔乘积,与92中的,一样
select b.*,bo.*
from beauty b
cross join boys bo;


练习题:
#查询编号>3的女神的男朋友信息,如果有则详细列出,如果没有,则显示为null
SELECT  b.name,bo.* FROM beauty b LEFT OUTER JOIN boys bo ON b.boyfriend_id=bo.id WHERE b.id>3;
#查询哪个城市没有部门
select city,department_name from locations l left outer join departments d on l.location_id
=d.location_id where d.department_id is null; #注意使用主键
#查询部门名为sal或it的员工技术信息
select e.* from employees e left outer join departments d on d.department_id= e.department_id
where department_name in ('SAL','IT');
视频中用的department作为主表的

 

 进阶7:子查询

含义:出现在其他语句中的select,成为子查询

外部的查询语句,成为主查询

分类:

按子查询出现的位置:

  select后面

      仅仅支持标量子查询

  from后面

      支持表子查询

  where或having后面★

      标量子查询✔

      列子查询✔

      行字查询

  exists后面(相关子查询)

      表子查询

按结果集的行列数不同“

标量子查询(结果集只有一行一列)

列子查询(结果集有一行多列)

行子查询(结果集有一行多列)

表子查询(结果集一般为多行多列)

一、where或having后面
1、标量子查询(单行子查询)
2、列子查询(多行子查询)
3、行子查询(多行多列)
特点:
1、子查询放在小括号内
2、子查询一般放在条件的右侧
3、标量子查询,一般搭配多行操作符使用
> < >= <= <>
列子查询,一般搭配着多行操作符使用
in,any/some,all

#1标量子查询

#案例1:谁的工资比Abel高
#1)查询abel的工资
select salary from employees where last_name='Abel';
2)查询员工的信息,满足salary>1)的结果
select * from employees where salary>(select salary from employees whrere
last_name ='Abel';)

#案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,和工资
select last_name,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 );

having
#案例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);
4、子查询的执行优先于主查询的执行

#非法使用标量子查询
tip:1>、<只能搭配标量子查询
2查询结果必须时一行一列


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

 

 

 

 #案例1: 返回location_id时1400或1700的部门中的所有员工姓名

#思路一:可以连表

#思路二:

select last_name from employees where department_id in(select

distinct department_id from departments where location_id=1400 or location_id=1700);#这里要去重!!

 

#案列2: 返回其他工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id以及salary 

select employee_id,last_name,job_id,salary from employees where salary <any(select distinct salary from employees where job_id ='IT_PROG') and job_id <> 'IT_PROG';

#返回其他工种中比job_id为‘IT_PROG’工种所有工资低的员工的员工号、姓名、job_id以及salary 

select employee_id,last_name,job_id,salary from employees where salary <all(select distinct salary from employees where job_id ='IT_PROG') and job_id <> 'IT_PROG';

tip :in 可以改成=any
   not in 可以改成 <> all

#3行字查询(结果集一行多列或多行多列) 使用较少
#案例:查询员工编号最小并且工资最高的员工信息
select max(salary) from employees where employee_id =(select min(emoployee_id) from
employees)#错误的写法

1查询最小的员工编号
select min(employee_id) from emoployees
2查询最高工资
select max(salary) from employees where employ_id=(
select min(employee_id) from emoployees
)
3查询员工信息
#老版查询
select * from employees where employee_id =(select min(employee_id) from employees )
and salary=(
select max(salary) from employees where employ_id=(
select min(employee_id) from emoployees))

#行子查询(结果一行多列或多行多列)
select * from employees where (employ_id,salary)=(
select min(employee_id),max(salary)
from employees);

 

 

二、select后面
只支持标量子查询,也就是说只支持一行一列 #案例1:查询每个部门的员工个数
select d.*,(select count(*) from employees group by department_id) from
department d;#错误,使用了分组
#难点
select d.*,(select count(*) from employees e where e.department_id =d.department_id
) 个数 from departments d;
执行顺序: from departments d -> select d*->(select count(*)..),需要理解()中
内容在循环的动态性,感觉第一句使用d.department_id也行啊!

#案例2:查询员工号=102的部门名
select department_name from departments
where department_id=
(select department_id from employees where employee_id=102)
;
select(select department_name from departments d inner join employees e on d.department_id
=e.department_id where e.employee_id=102) 部门名;



 

三、from后面
tip:将子查询结果充当一张表,要求必须起别名 #案例:查询每个部门的平均工资的工资等级 #1查询每个部门的平均工资
select avg(salary),department_id employees group by department_id ; 这里是一张表! #2连接1的结果集和job_grade表,筛选条件平均工资between lowsest_sal and highest_sal
难点
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后面(相关子查询)
结果1或0
select exists(select employee_id from employees) ;
#案例1:查询有员工的部门名
难点:
select department_name from departments d where exists

(select employee_id from employees where department_id=d.department_id);

#in
select department_name from departments d where d.department_id in(select department
_id from employees))


#案例2:查询没有女朋友的男神信息
#exists
select *from boys where not exists(select boyfriend_id from beauty where boys.id
=beauty.boyfriend_id);
#in
select bo.* from boys bo where bo.id not in (select boyfriend_id from beauty);

根据不同的情况选择子查询是放在select、from、exists的后面

练习:
1、查询和Zlotkey相同部门的员工姓名和工资
select last_name,salary from employees where employees.department_id = (
select department_id from employees where last_name='Zlotkey');
2、查询工资比公司平均工资高的员工的员工号,姓名和工资
select employee_id,last_name,salary from employees where salary > (
select avg(salary) from employees );

3、查询各部门中工资比本部门平均工资高的员工的员工工号,姓名和工资

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

4、查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名

select employee_id,last_name from employees where department_id in
(select distinct department_id from employees where last_name like '%u%')
and last_name not like '%u%';#不要忘记去重
5、查询在部门的location_id 为1700的部门工作的员工的员工号
select employee_id from employees where department_id in (select distinct department_id
from departments where location_id=1700)#或者将in写成=any
6、查询管理者是King的员工姓名和工资
select last_name,salary from employees where manager_id in (select employee_id from
employees where last_name='King' );
7、查询工资最高的员工的姓名,要求first_name和last_name显示为一列 ,列名为姓.名
select concat(last_name,first_name) 姓.名 from employees where salary =
(select max(salary) from employees );

 进阶8:分页查询⭐

应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求

语法:

select 查询列表 from 表

【join type】 join 表2 

on 连接条件

where 筛选条件

groupby 分组字段

having 分组后的筛选

order by 排序的字段

limit  offset(要显示条目的起始索引,从0开始),size(要显示的条目个数);

#案例1:查询前5条员工的信息

select * from employees limit 0,5;

select * from employees limit5 5 ;

#案例2;查询第11条-25条

。。limit10,15;

 

#案列3:有奖金的员工信息,并且工资较高的前10名显示出来

select * from employees where commission_pct is not null order by salary desc limit 10;

#测试题
一、查询所有学员的邮箱的用户名
select substr(email,1,instr(email,'@')-1) 用户名 from stuinfo;
instr 返回字符串出现的索引,substr第二个参数是长度

#1.查询工资最低的员工信息:last_name,salary
select last_name,salary from employees where salary in(select min(salary)
from employees) ;

2.查询平均工资最低的部门信息
select * from department where department_id=(
select department_id from employees group by department_id having min(avg(salary)));
#上面的错误
select d.*,min(k.avs) from departments d inner join (select avg(salary) avs ,department_id from employees group by
department_id ) k on d.department_id = k.department_id;#这里没注意分组和聚合函数的使用,所以错误
#正解,要注意主表

SELECT d.*,MIN(k.avs) FROM (SELECT AVG(salary) avs ,
department_id FROM employees GROUP BY
department_id ORDER BY avs ) k INNER JOIN departments d ON d.department_id = k.department_id;

 


3.查询平均工资最低的部门信息和该部门的平均工资
select department_id from employees group by department_id having
min(avg(salary)); #感觉行不通

select * , (select avg(salary) from employees where department_id
=department_id)
 from departments  ; #感觉又卡了

select *,min(avg(salary)) from departments d inner join employees e on d.department_id =
e.department_id group by department_id ;
#平均工资
select avg(salary),department_id from employees group by department_id;

SELECT d.*,MIN(k.a)FROM (SELECT AVG(salary) a,department_id FROM employees GROUP BY department_id ORDER BY a ASC limit 1;
) k INNER JOIN departments d
ON d.department_id = k.department_id;


4.查询平均工资最高的job信息
select * from jobs j inner join (select avg(salary),job_id from
employees group by job_id) k on j.job_id=k.job_id where k.avg(salary);#行不通
#可以使用order by+limit
这方法存在缺点:有两个个job就不行了,还是老老实实子查询嵌套吧

SELECT j.*,MAX(k.avs) FROM jobs j INNER JOIN (SELECT AVG(salary) avs,job_id FROM
employees GROUP BY job_id order by avg(salary) desc ) k ON j.job_id=k.job_id ;

//select d.*,avg(salary) from (select avg(salary)  from employees e where e.department_id = d.department_id ) from
//departments d; #本质上是连表查询

5.查询平均工资高于公司平均工资的部门有哪些
少了null 视频中是将employees作为主表的
select d.* from departments d inner join (select distinct department_id from employees group by department_id
having (avg(salary))> (select avg(salary) from employees) ) k on d.department_id =k.department_id;
6.查询出公司中所有manager的详细信息
select *from employees where employee_id in(select distinct manager_id from employees)
7.各个部门中最高工资中最低的的那个部门的最低工资是多少
select min(k.ms) from (select max(salary) ms from employees group by department_id)
k;#这个必须要起别名
少了一步,查询那个部门的最低工资是上面的结果
8.查询平均工资最高的部门manager的详细信息:last_name,department_id,email, salary
#查询平均工资最高的部门编号
#将employees和departments连接查询,筛选条件是1

SELECT e.*FROM employees e WHERE employee_id IN(SELECT manager_id FROM departments d INNER JOIN
(SELECT MAX(s.avs),department_id FROM (SELECT AVG(salary) avs,department_id FROM employees WHERE department_id IS NOT NULL
GROUP BY department_id) s) k ON d.department_id = k.department_id);

标准:
select
    last_name..
from
    employees e
inner join
    departments d on d.manager_id = e.employee_id
where d.department_id =
(select department_id from employeees group by department_id order by avg(salary) desc )
limit 1;

  DQL语言的最后一部分:union联合查询

 进阶9:联合查询

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

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

特点:1、要求多条查询语句的列数是一致的

   2、要求多条查询语句的查询的每一列的类型和顺序最好一致

   3、union关键字默认去重,如果使用uinon 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,t_Name,tGender From t_ua where tGender='male';

 

 

 

#DML语言

/*数据操作语言

插入:insert

修改:update

删除:delete

 

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

#2不可以为null的列必须插入值,可以为null的值如何插入值?
方式一:null
方式二:省略可谓null的字段
insert into beauty(id,name,sex,phone)
values(15,'娜扎','女','13888888888');

3#列的顺序是否可以调换
可以,但是要一一对应
4#可以省略列名,默认所有列,而且列的顺序表和表中列的顺序一致,注意nullbable不能省略
insert into bearty values(18,'zhangfei','男',null,'119',,null,null)

#另一种插入方式
insert into 表名 set 列名=值,列名=值,...
insert into beauty set id=19,name='刘涛',phone='999';

#两种方式的对比
1、方式一支持插入多行,方式二不支持
insert into beauty
values (23,'唐艺昕','女','1990-4-23','12345678901,null,2),
(24,'唐艺昕','女','1990-4-23','12345678901,null,2),
(25,'唐艺昕','女','1990-4-23','12345678901,null,2),
2、方式一支持子查询,方式二不支持
insert into beauty(id,name,phone)
select 26,'宋茜','12345678901' union
select 15,'','';#单单这一句是查询常量,不涉及表

二、修改语句
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表中姓唐的女神的电话为13833344455 
update beauty set phone= '13833344455' where
name = '唐%';
#案例2:修改boys表中的id号为2的名称为张飞,美丽值为10;
update boys set boyname= '张飞',usercp=10 where id =2;

#2修改多表的记录
#案例1:修改张无忌的女朋友的手机号为114
update boys b inner join beauty g on b.id = g.boyfriend_id
set g.phone='114' where b.name = '张无忌';
#案例2:修改没有男朋友的女神的男朋友的编号都为2号
update beauty b left join boys o on b.boyfriend_id =o.id set
b.boyfriend=2 where b.id is null; #is null 而不能用=nul!

三、删除语句

方式一:delete

删除的肯定是表中的一条条记录,所以直接使用表名而不是使用字段

语法:

1、单表的删除

delete from 表名 where 筛选条件

2、多表的删除【补充】(级联删除)

sql92语法

delete 别名

from 表1 别名,表2 别名

where 连接条件

and 筛选条件;

sql99语法

delete 别名

from 表1 别名 inner|left|right join 表2  别名 on 连接条件 where 筛选条件;

方式二:truncate

语法:truncate table 表名;

#方式一:delete 
#1、单表的删除
#案例1:删除手机号以9结尾的女神信息
delete from beauty where phone like '%9';

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

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

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

#delete form my_employees 删除所有数据

#方式二:truncate语句
清空数据
#案例:将魅力值>100的男神信息删除
truncate tables boys;

两种方式的对比:
1、delete 是可以加where条件,truncate不能加
2、使用truncate效率高一点
3、假如说要删除的表中有自增长列,自增长列的值从断点开始,
而truncate删除后再插入数据,自增长列的值从1开始。
4、delete有返回值,truncate没有返回值
5、truncate删除不能回滚,delete删除可以回滚【面试题】

delete from beauty limit 1; 也是删除了一条



 

 

#DDL语言

/*

数据定义语言

库和表的管理

一、库的管理

创建、修改、删除

二、表的管理

创建、修改、删除

创建:create

修改:alter

删除:drop (区别于delete,delete是对数据的操作语句,drop是对表和库的操作语句)

#一、库的管理
#1、库的创建
语法:
create database 库名;
#创建books库
create database if not exists books 【character set 字符集名】;

#2、库的修改
rename database books to 新库名;#这句已经不能用了,可能会造成数据的丢失
                   #修改文件名

更改库的字符集
alter database books character set gbk;

#3、库的删除
drop database if exists books;


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

#创建book表
create table if not exists book(
          id int,
          bName varchar(20),
          price double,
          authorId int,
          publishDate datetime);
create table author( id int, au_name varchar(20),nation varchar(10)); 

#2、表的修改

/*alter table 表名 add|drop|modify|change column 列名 【列类型 约束】;
*/
#(1)修改列名 column可以省略,也可以改类型
alter table author change column au_name(旧列名) author_name(新列名) varchar(20); #这里必须要指定类型!

#(2)修改列的类型或约束
alter table book modify column pubdate timestamp;modify column 列名 新类型

#(3)添加新列
alter table author add column annual double 【first|after 字段名】;
#(4)删除列
alter table author drop column annual;
#(5)修改表名
alter table author rename 【to】 book_author;

#3、表的删除
drop table book_author ;
show tables;
if (not)exists 仅在表的创建和删除的时候使用

#通用的写法:
drop database if exists 旧的库名;
create database 新库名;

drop table if exists 旧表名;
create table 表名();

#4、表的复制


INSERT INTO book_author VALUES(1,'村上春树','日本'),
(2,'moyan','中国'),
(3,'冯唐','中国'),
(4,'金庸','中国');

#1.仅仅复制表的结构

create  table copy like author;

#2.复制表的结构和数据

create table copy2  select * from book_author;

#只复制部分数据

  CREATE TABLE copy3 SELECT id,authorname FROM book_author WHERE
  nation='中国';

  #仅仅复制某些字段

  create table copy4 select  id,authorname from book_author where 0; 



 

#练习题
(跨库)
#2、将departments中的数据插入新表dept2中
create table dept2 select * from myemployees.departments;
myemployees是库名,因为题目中dept2和departments不在一个库中
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 if exists emp5;
7、将表employees2重新命名为emp5
alter table if exists employee2 rename to emp5;
8、在表dept和emp5中添加新列test_column,并检查所作的操作
alter table dept add column test_column int;
alter table emp5 add column test_column int;
9、直接删除表emp5中的列dept_id
alter table emp5 drop column dept_id;
 

##常见的数据类型

/*

数值型:

整型

小数:定点数 浮点数

字符型:较短的文本:varchar 、char

    较长的文本:text ,blob(较长的二进制数据)

日期型:

*/

一、整型

/*

分类:tinyint,   smallint,   medium,  int/integer,  bigint

特点:

1、默认设置有符号,如果想添加无符号需要添加unsigned

2、如果插入的数值超出了整型的范围,会报 out of range 警告,插入临界值

3、如果不设置长度,会有默认长度(区别于字节数,字节数由类型决定,这个只是决定显示的长度)
长度代表显示的最大宽度,如果不够用会用0在左边填充,但必须搭配zerofill使用
*/ #如何设置有符号和无符号 drop table if exists t_int; create table t_int(t1 int ,t2 int unsigned); desc t_int; #设置显示的宽度 create table tab_int(t1 int(7) zerofill,t2 int(7) zerofill unsigned)



二、小数

 

 

 

/*
1、浮点型
float(M,D)
double(M,D)
2、定点型
dec(M,D)
decimal(M,D)
特点:
(1)M和D D是小数点后的位数,M是整数部位+小数部位的总长度
如果超过范围,则插入临界值
(2)M和D都可以省略
如果是定点型decimal,则M默认是10,D默认是0
如果是float和double,则会根据插入的数值的精度来决定精度

(3)定点型的精确度较高,如果要求插入数值的精度较高如货币运算考虑
*/ create table tab_float(f1 float(5,2), f2 double(5,2), f3 decimal(5,2));

insert into tab_float values(123.45,123.45,123.45);
INSERT INTO tab_float VALUES(123.456,123.456,123.456);

#原则:
/*所选择的类型越简单越好
*/

三、字符型

/*较短的文本:

char

varchar

其他:binary和varbinary用于保存较短的二进制

enum用于保存枚举

set用于保存集合

较长的文本:

text

blob(较大的二进制)

特点:

写法 char(M)    M的意思 最大的字符数  ,可以省略,默认为1    char效率高,耗费空间

   varchar(M)  M的意思 最大的字符数,不可以省略       varchar效率低,节省空间

#enum类型


CREATE
TABLE tab_char( c1 ENUM('a','b','c')); INSERT INTO tab_char VALUES('a'); INSERT INTO tab_char VALUES('A');#可以,但插的是a INSERT INTO tab_char VALUES('m');#失败

#set类型

create table tab_set(s1 set('a','b','c','d'));

insert into tab_set values('a');
insert into tab_set values('a','b');

#四、日期型

/*

分类:

date只保存日期

time只保存时间

year只保存年

datetime保存日期+时间

timestamp保存日期+时间

特点:

      字节         范围       时区等的影响

datetime        8                      1000-9999       不受

timestamp      4      1970-2038      受

 tip:这里的受影响 是指插入数据以后 再修改时区 datetime不会变化而timestamp会随着时区的变化而变化

create table tab_date (
t1 datetime,
t2 timestamp  #注意这里不能有逗号
);
insert into tab_date values(now(),now());
select * from tab_date;

show variables like 'time_zone';

SET time_zone='+9:00';

#常见约束

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

create  table 表名(字段名 字段类型 约束);

分类:六大约束

not lull :非空约束

default:默认

primary key :主键,用于保证该字段的值具有唯一性并且非空

uique:唯一,用于保证该字段的值具有唯一性,可以为空

check:检查约束【mysql不支持】

foreign key :外键,用于限制两个表的关系,用于保证改字段的值必须来自于主表的关联列的值     在从表中添加外键约束,用于引用主表的某列的值

添加约束的时机:

1、添加表时

2、修改表时

约束的添加分类:

列级约束:

六大约束语法都支持,但外键的约束没有效果

create  table 表名(字段名 字段类型 列级约束,

              字段名 字段类型,)

表级约束:

除了非默认,其他的都支持

#一、创建表时添加约束
#1、添加列级约束
/*语法
直接在字段名和类型后面追加约束类型即可
只支持:默认、主键、非空、唯一
create database student;

CREATE TABLE stuinfo( id INT PRIMARY KEY, stuName VARCHAR(20) NOT NULL, sex CHAR(1) CHECK(gender = '男' OR gender ='女'), #无效 seat INT UNIQUE, age INT DEFAULT 18, majorId INT REFERENCES major(id)); #无效

des stuinfo;
SHOW INDEX FROM stuinfo;#查看表中的索引包括(主键、外键、唯一)


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

DROP TABLE IF EXISTS stuinfo; CREATE TABLE stuinfo ( id INT, stuname VARCHAR(20), gender CHAR(1), seat INT,
seat2 int, age INT, majorid INT, CONSTRAINT pk PRIMARY KEY(id),#主键
... primary key(id,stuname)#联合主键,将id和stuname设置为联合主键 CONSTRAINT uq UNIQUE(seat),#唯一键
...unique(seat,seat2) CONSTRAINT ck CHECK(gender='男' OR gender='女'),#检查 CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id));#外键 #不起名默认为字段名称,但是主键名称始终时primary
SHOW INDEX FROM stuinfo;

#通用的写法:
create table if not exists stuinfo(
id int primary key,
stuname varchar(20) not null,
sex char(1),
age int default 18,
seat int unique,
majorid int,
constraint fk_stuinfo_major foreign key(majorid) references major(id)#外键
);

主键和唯一的大对比:
    保证唯一性 是否允许为空
主键 是       否
唯一 是        是

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


#二、修改表的约束
/*添加列级约束不支持起别名
1、添加的是列级约束
alter table 表名 modify column 列名 字段类型 新约束
#1、添加非空约束
alter table studinfo modify column stuname varchar(20) not null;
#2、添加默认约束
alter table stuinfo modify column age int defalut 18;
#3、添加主键(因为主键支持列级约束和表级约束两种写法)
(1)列级约束
alter table stuinfo modify column id int primary key;
(2)表级约束
alter table stuinfo add primary key(id);
#4、添加唯一
(1)列级约束
alter table stuinfo modify column seat int unique;
(2)表级约束
alter table stuinfo add primary key(id);
#5、添加外键
alter table stuinfo add constraint fk_stuinfo_major foreign key(majorid) references major(id);
#三、修改表时删除约束
#1、删除非空约束
alter table stuinfo column stuname varchar(20) null;
#2、删除默认约束
alter table stuinfo modify column age int; #原本是default 18 ,现在删除掉默认约束了;
#3、删除主键
alter table stuinfo modify column id int;#同样是不写 ??有问题
alter table stuinfo drop primary key;#类似表级约束
#4、删除唯一
alter table stuinfo drop index seat;
show index from stuinfo;查看唯一键的名称
#5、删除外键约束
alter table stuinfo drop foreign key fk_stuinfo_major;

        位置         支持的类型              是否可以起约束名
列级约束:  列的后面        语法都支持,但外键没有效果      不可以
表级约束:  所有列的下面      默认和非空不支持,其他支持       可以(主键没有效果)



 #标识列

/*

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

特点:

1、标识列必须和主键搭配吗?

不一定,但要求是一个key(主键,唯一,外键,自定义)

2、一个表中可以有几个标识列?

3、标识列的类型

类型必须是数值型(float、int、double...)

4、标识列可以通过 手动插入设置起始值

一个

*/

 

#一、创建表时的设置标识列
create table tab_identity(
id int primary key   AUTO_INCREMENT,
name varchar(20)
);
insert into tab_identity(id,name) values(null,'john');#这里虽然插入的是null,
但是有自增约束会自动添加id;
或者直接省略:
insert into tab_identity(name) values('lucy');

show variables like '%auto_increment%';#下面第一行是步长,第二行是起始地址

 


 SET auto_increment_increment=3;#改变步长,再次插入时就会改变步长

#truccate table tab_identity;
insert into tab_identity(id,name) values(10,'john'); #这一步指定了起始索引

二、修改表时设置标识列
alter table tab_identity modify column id int auto_increment;
#如果已有主键,上句不能带primary key!

三、修改表时删除标识列
alter table tab_identity modify column id int ;#不会去掉主键

 

 #TCL

/*

Transaction Control Language  #事务控制语言

事务:

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

案例:

 

 

 

 

 

 

 事务的创建

隐式事务:事务没有明显的开启和结束的标记

比如insert、update、delete语句

 显示事务:事务具有明显的开启和结束的标记

前提:必须先设置自动提交功能为禁用

 set autocommit=0; 只针对当前事务有效

步骤一:开启事务

set autocommit=0;

start transaction;#可选的

步骤二:编写事务的sql语句(select、insert、update、delete)DDL语言没有事务之说

语句1;

语句2;

。。。

步骤三:结束事务

commit;提交事务

rollback;回滚事务

savepoint 节点名

#演示事务的使用步骤

set autocommit=0;

start transaction;

#编写一组事务的语句

update account set balance=500 where username='张无忌';

update account set balance=1500 where username='赵敏';

#结束事务

commit;

#rollback;

select * from account;

#2、delete 和truncate在事务和使用时的区别

#演示delete; 支持回滚

set autocommit=0;

start transaction;

delete from account;

rollback;

#演示truncate  不支持回滚

set autocommit=0;

start transaction;

truncate table account;

rollback;

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

 

#演示savepoint的使用

set autocommit=0;

start transaction;

delete from account where id=25;

savepoint a;#设置保存点

delete from account where id=28;

rollback to a;#回滚到保存点

 

 

 

 

 

 

 

 select  @@tx_isolation;

set session transaction isolation level read uncommitted;  mysql 8.0 改成@@transaction_isolation

set session transaction isolation level repeatable read;

脏读:事务回滚  不可重复读:事务提交前后的对比

 

 

 事务的隔离级别:

read uncommitted:

read committed:

repeatable read:

serializable:

mysql 默认第三个,oracle中默认第二个

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

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

 

#视图

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

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

 

视图   create view ...

表     create table ...

特点:

*/

 

 #案列:查询姓张的学生名和专业名

select stuname,majorname from stuinfo s inner join major  m on  s.majorid=m.id

where s.stuname like '张%';

create view vq as select stuname,majorname from stuinfo s inner join major m on s.majorid

=m.id;

select * from v1 where stuname like '张%';

一、创建视图
语法:create view 视图名  as 查询语句;

1、查询邮箱中包含a字符的员工名、部门名和工种信息
#创建
create view  myv1  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 j.job_id = e.job_id;
#使用
select * from myv1 where last_name like '%a%';

#二、视图的修改
#方式一:
create or replace view 视图名 as 查询语句;
##select * from myv3 ;
create or repalce 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;
#四、查看视图
desc myv3;
show create view myv3;#展示创建视图的语句
#五、视图的更新(修改视图表中的数据)
create or replace view myv1
as
select last_name,email,salary*12*(1+ifnull(commission_pct,0))
annual_salary from employees;

会修改原来的数据
#1、插入
isnert into myv1 values('张飞','af@qq.com',10000);
#2、修改
update myv1 set last_name='张无忌' where last_name='张飞';
#3、删除
delete from myv1 where last_name='张无忌';

#具备以下特点视图是不能更新的

#1、包含以下关键子的sql语句:分组函数,distinct,group by,having,union或者
union all
#2、常量视图
create or replace view myv2 as select 'john' name;(name 是别名)

#3、select中包含子查询
create or replace view myv3 as select (select max(salary) from employees)
最高工资;

#更新(注意都是没有办法更新的)
select * from myv3;
update myv3 set 最高工资=10000;

#4、join
create or replace view myv4 as
select last_name,department_name
from employees e join departments d on e.department_id=d.department_id;


#5、from一个不能更新的视图
create or repalce view myv5 as select * from myv3;
#select * from myv5;
update myv5 set 最高工资=10000 where department_id=60;

#6、where子句的子查询 引用了from子句的表
create or repalce view myv6 as select last_name,email,salary from employees
where employee_id in (
select manager_id from employees where manager_id is not null);


#变量

/*

系统变量:全局变量、会话变量

自定义变量:用户变量、局部变量

*/

#一、系统变量
说明:变量由系统提供,不是用户定义,属于服务其层面使用的语法
1、查看所有的系统变量
show global 【session】 variables; #session不写代表默认session

2、查看满足条件的部分系统变量;
show global 【session】 variables like '%char%';
3、查看指定的某个系统变量的值
select @@global|【session】.系统变量名;
4、为某个系统变量赋值
方式一:
set global|【session】系统变量名=值;
方式二:
set @@global |【session】.系统变量名=值;

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

#1》全局变量
/*
作用域:服务器每次启动将为所有的全局变量赋初始值,针对于多有的会话(连接)有效的
但不能跨重启
*/
#1、查看所有的全局变量
show global variables;
#2、查看部分的全局变量
show global variables like '%char%';
#3、查看指定的全局变量的值
select @@global.autocommit;
select @@tx_isolation;

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

#2》会话变量
/*
作用域:仅仅针对于当前会话(连接)有效
*/
#查看所有的会话变量
show variables;
show session variables;
#查看部分的会话变量
show variables like '%char%';
show session variables like '%char%';
#查看指定的某个会话变量
select @@tx_isolation;
select @@session.tx_isolation;
#为某个会话变量赋值
方式一:
set @@session.tx_isolation='read-uncommitted';
方式二:
set session tx_isolation = 'read-uncommitted';

#二、自定义变量
/*
说明:变量是用户自定义的
使用步骤
声明
赋值
使用(查看、比较、运算等)
*/
#1、用户变量
/*
作用域:针对于当前(连接)有效,同于会话变量的作用域
应用在任何地方,也就是begin、end里面 或者begin、end的外面
*/
赋值的操作符有两个:=或:=
#1、声明并初始化
set @用户变量名=值;或
set @用户变量名:=值;或
select @用户变量名:=值;
#2、赋值(更新用户变量的值)
方式一:通过set或select
set @用户变量名=值;
set @用户变量名:=值;
select @用户变量名:=值;
方式二:通过select into
select 字段 into @变量名 from 表;

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

select count(*) into @count from employees
#3、使用(查看用户变量)
select @用户变量名;
select @count;

#2、局部变量
/*
作用域:仅仅在定义它的begin end中有效
应用在begin、end中
*/
#声明
declare 变量名 类型;
declare 变量名 类型 default 值;

#赋值

方式一:通过set或select
set 局部变量名=值;
set 局部变量名:=值;
select @局部变量名:=值;
方式二:通过select into
select 字段 into 局部变量名 from 表;

#3、使用
select 局部变量名;

对比用户变量和局部变量:
      作用域    定义和使用位置              语法
用户变量 当前会话    当前会话的任何地方           必须加@符号,不用限定类型 
局部变量 begin end中  只能在begin end中,且为第一句话   一般不用加@符号,需要限定类型 

案例:声明两个变量并赋初始值,求和,并打印
#1、用户变量
set @m=1;
set @n=2;
set @sum=@m+@n;
select @sum;
#2、局部变量
declare m int default 1;
declare n int default 2;
set sum=m+n;
select sum;


#存储过程和函数
/*
存储过程和函数:类似于java中的方法
好处:
1、提高代码的重用性
2、简化操作
*/
#存储过程
/*
含义:一组预先编译好的sql语句的集合,理解成批处理语句
1、
2、
3、减少了编译次数并且减少了和数据库的服务器的连接次数,提高了效率
*/
#一、创建语法
create procedure 存储过程名(参数列表)
begin
    存储过程体(一组合法的sql语句)

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

#1、空参列表
#案例:插入到admin表中的五条记录
select * from admin;

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

#调用
call.mypl()$ #这里mysql中需要加$,但是mysqlyog中没有加哎

#案列1:创建存储过程的实现 根据女神名 ,查询对应的男神信息

create procedure myp2(in beautyname varchar(20))
begin
    select bo.* from boys bo right join beauty b on bo.id=b.boyfriend_id
    where b.name=beautyname;
end $

#调用
call myp2('柳岩')$ #可能会产生字符集的问题

 

 set name gbk;


#案例二:创建存储过程实现,用户是否登录成功
create prpcedure 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:根据女神名,返回对应的男神名
create procedure myp5(in beautyname varchar(20),out boyname varchar(20))
begin
    select bo.boyname into buyname
    from boys.bo inner join beauty b bo.id=b.boyfriend_id
    where b.name=beautyname;

end $

#调用
set @bname$
call myp5('校招',@bname)$()因为全局修改了结束符为$
select @bname$

#案例2:根据女神名,返回对应的男神名和男神魅力值
create precedure myp6(in beautyname varchar(20),out boyname varchar(20),
out usercp int)
begin
    select bo.boyname,bo.usercp into boyname,usercp 
    from boys.bo inner join beauty b bo.id=b.boyfriend_id
    where b.name=beautyname;
end $

#调用
call myp6('校招',@bname,@usercp)$
查看
select @bname,@usercp$

创建带inout模式参数的存储过程
#案例1:传入a和b两个值,最终a和b都翻倍并返回
create procedure myp8(inout a int,inout b int)
begin
   set a = a*2;
   set b = b*2;

     
end $ #(end;$也可)


定义两个用户变量
set @m=10$
set @n=20$
call myp8(@m,@n)$
select @m,@n$

练习题:
1、创建存储过程或函数实现传入两个女神生日,返回大小
create procedure test_pro3(in birth1 datetime,in birth2 datetime,out result int)
begin
    select datediff(birth1,birth2) into result ;
end$

2、创建存储过程或函数实现传入一个日期,格式化成xx年xx月xx日
create procedure myp9(in date mydate,out strdate varchar(20) )
begin
    select date_format(mydate,'%y年%m月%d日') into strdate;    

end $
call test_pro3(now(),@str)$
select @str $
3、创建存储过程或函数实现传入女神名,返回:女神and男神 格式的字符串
如 传入:小昭
返回:小昭 and 张无忌
create procedure teest_pro5(in beautyname varchar(20),out str varchar(50))
begin
   select concat(beautyname,'and',ifnull(boyname,'null')) into str
   from boys bo right join beauty b on bo.id=b.boyfriend_id where b.name
    =beautyname;
end$
call test_pro5('小昭',@str)$
select @str $

4、创建存储过程或函数,根据传入的条目数和起始索引,查询beauty表的记录
drop procedure test_pro6$
create procedure test_pro6(in startIndex int , in size int)
begin
    select *from beauty limit startIndex,size;
end $
call test_pro6(3,5)$
二、删除存储过程
语法:drop procedure 存储过程名
drop procedure p1;
drop procedure p2,p3;

三、查看存储过程信息
desc myp2;
show create procedure myp2;

#函数

/* 含义:一组预先编译号的sql语句的集合,理解成批处理语句

1、提高代码的重用性

2、简化操作

3、减少了编译次数并减少了和数据库服务器的连接次数,提高了效率

区别:

存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新

函数:有且仅有一个返回,适合做处理数据后返回一个结果

 

#一、创建语法
create function 函数名(参数列表) returns 返回类型
begin 
    
        函数体
end
/*
注意:
1、参数列表 包含两部分:
参数名 参数类型

2、函数体:肯定会有return语句,如果没有会报错
如果return语句没有放在函数体的最后也不会报错,但不建议
return 值;
3、函数体中仅有一句话,则可以省略begin end
4、使用delimiter语句设置结束标记
注意:不要写成delimiter $;  #这样需要以$;结尾。而且是针对一个客户端
的,如果在只mysql客户端修改,则在sqlyog客户端并没有效果

*/

#二、调用语法
select 函数名(参数列表)#执行函数,并且显示返回值

#案列演示
1、无参有返回的
#案例:发布会公司员工的个数
create function myf1() returns int
begin
  declare c int default 0;#定义变量
  select count(*) into c #赋值
  from employees;
  return c; #返回
    
end $

select myf1()$ #

2、有参有返回
#案例1:根据员工名,返回它的工资
create funciton myf2(empname varchar(20)) returns double
begin
    set @sal=0;#定义用户变量 ,这里也可以使用局部变量
    select salary
    from employees
    where last_name=empname;

    return @sal;
end $

select myf2('kochhar')$

#三、查看函数的定义
show create function myf3;
#四、删除函数
drop function myf3;
#一、分支结构
#1、if函数
select if(表达式1,表达式2,表达式3)
如果表达式1成立,则返回表达式2的值,否则返回表达式3的值
#2、case 结构
情况1:类似于java中的switch语句,一般用于实现等值判断
语法:
  case 变量|表达式|字段
  when 要判断的值 then 返回的值1(或语句1;)
  when 要判断的值 then 返回的值2(或语句2;)
  ...
  else 要返回的值n(或语句;)
  end (case;)
then后面如果跟语句的要加分号且最后的end改为end case;//上面的括号标表示有语句的情况

情况2:类似于java中多重if语句,一般用于实现区间判断
语法:
case 变量|表达式|字段
  when 要判断的条件1 then 返回的值1(或语句1;)
  when 要判断的条件2 then 返回的值2(或语句2;)
  ...
  else 要返回的值n(或语句n;)
  end (case;)
特点:
1、可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或begin end外面
可以作为独立的语句去使用,只能放在begin end中(需加分号)
2、如果when中的值满足或条件成立,则执行对应的then后面的语句,并且结束case
如果都不满足,则执行else中的语句或值
3、else省略,如果else省略了,并且所有when条件都不满足,则返回null

#案列
#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100显示A...
create procedure test_case(in score int)
begin
  case
  when score>=90 and score<=100 then select 'A';
  when score>=80 then select 'B';
  when score>=60 then select 'C';
  else select 'D';
  end case;
  
end $
call test_case(95)$

#3、if结构
/*
功能:实现多重分支
if 条件1 then 语句1;
elseif 条件2 then 语句2;
。。。
【else 语句n;】
end if;
应用在begin end中
*/

#案例1:
create function test_if(score int) return char
begin
    if score >=90 and score<=100 then return 'A';
    elseif score>=80 then return 'B';
    elseif score>=60 then return 'C';
    else return 'D';
    end if;
end$



 

#流程控制结构

/*顺序结构:程序从上往下依次执行

分支结构:程序从两条或多条路径中选择一条去执行

循环结构:程序在满足一定条件的基础上,重复执行一段代码

*/

#二、循环结构
/*
分类:
while、loop、repeat
循环控制:
iterate类似于continue,继续,结束本次循环,继续下一次
leave类似于break,跳出,结束当前所在的循环
*/
#1while
/*
语法:
【标签:】while 循环条件 do 循环体;
end while 【标签】;


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


#2、loop
语法:【标签:】 loop 
                循环体;
end loop 【标签】;
可以用来模拟简单的死循环

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

#案例:批量插入,根据次数插入到admin表中多条记录
create procedure pro_while1(in insertcount int)
begin
    declare i int default 1;
   a: while i<= insertcount do
        insert into admin(username,password) values(
         concat('Ross',i),'666');
    set i =i+1;
    end while a;


end $
call pro_while1(100)$

#2、添加leave语句
#案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
truncate table admin$
drop procedure test_while1$
create procedure test_while1(in insertcount int)
begin
    declare i int default 1;
    a:while i <=insertcount do
      insert into admin(username,password) values(
      concat('xiaohua',i),'0000');
      if i>=20 then leave a;
      set i =i+1;
      end if;
    end while a;
    
end $
call test_while1(100)$

#3、添加iterate语句

#案例:批量的插入,根据次数插入到admin表中多条记录,只插入偶数次
truncate table admin$
drop procedure test_while1$
create procedure test_while1(in insertcount int)
begin
    declare i int default 1;
    a:while i <=insertcount do
      set i=i+1;
      if mod(i,2)!=0 then iterate a;
      end if;
      insert into admin(username,password) values(
      concat('xiaohua',i),'0000');
      if i>=20 then leave a;
      
      end if;
    end while a;
    
end $
call test_while1(100)$

 

 

一、单表查询

前期表与数据准备

# 创建一张部门表
create table emp(
  id int not null unique auto_increment,
  name varchar(20) not null,
  sex enum('male','female') not null default 'male', #大部分是男的
  age int(3) unsigned not null default 28,
  hire_date date not null,
  post varchar(50),
  post_comment varchar(100),
  salary double(15,2),
  office int, # 一个部门一个屋子
  depart_id int
);
# 插入记录
# 三个部门:教学,销售,运营
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('tank','male',17,'20170301','形象代言部门',7300.33,401,1), 

('egon','male',78,'20150302','teacher',1000000.31,401,1), # 以下是教学部
('kevin','male',81,'20130305','teacher',8300,401,1),
('jason','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jerry','female',18,'20110211','teacher',9000,401,1),
('大饼','male',18,'19000301','teacher',30000,401,1),
('sean','male',48,'20101111','teacher',10000,401,1),

('歪歪','female',48,'20150311','sale',3000.13,402,2),# 以下是销售部门
('丫丫','female',38,'20101101','sale',2000.35,402,2),
('丁丁','female',18,'20110312','sale',1000.37,402,2),
('星星','female',18,'20160513','sale',3000.29,402,2),
('格格','female',28,'20170127','sale',4000.33,402,2),

('张野','male',28,'20160311','operation',10000.13,403,3), # 以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);

# PS:如果在windows系统中,插入中文字符,select的结果为空白,可以将所有字符编码统一设置成gbk

1、语法书写顺序与执行顺序

# 在写SQL命令是注意两点:
	- 书写顺序
		- select 字段1, 字段2...
		- from 表名
		- where 查询条件
		
select * from emp where id > 3 and id < 6;

	- 执行顺序:
     - from ---> 找到图书馆
        - where ---> 找到书的位置
        - select ---> 找到书本中的某一页

#比如: 图书管理员,得先找到是哪一个图书馆(哪张表),
#再找这本书在图书馆的哪个位置(哪一条记录), 最后查找这个本书中某一页(哪些字段值);
           
# 注意: 执行顺序必须要清楚!!!

查询数据量大时,可以在表后面 + \G,修改显示格式;
select * from emp\G  # 不要加分号

2、where约束条件

1.查询id大于等于3小于等于6的数据
# and: 与
select * from emp where id>=3 and id <=6;
# between: 两者之间
select * from emp where id between 3 and 6;


2.查询id小于3或者大于6的数据
select * from emp where id < 3 or id > 6;
select * from emp where id not between 3 and 6;


3.查询薪资是20000或者18000或者17000的数据
# or:  或者
select * from emp where salary=20000 or salary=18000 or salary=17000;
# in: 在什么里
select * from emp where salary in (20000, 18000, 17000);


4.查询薪资不在20000,18000,17000范围的数据
select * from emp where salary not in (20000, 18000, 17000);


5.查询员工姓名中包含o字母 的 员工姓名和薪资
# like: 模糊匹配
# %: 匹配0个或多个任意字符
# _: 匹配一个任意字符
select name,salary from emp where name like '%o%';


6.查询员工姓名是由四个字符组成的员工姓名与薪资
select name,salary from emp where name like '____';
# char_length(字段): 获取字段长度
select name, salary from emp where char_length(name)=4;


7.查询岗位描述为空的员工名与岗位名 
# 注意: 针对null不能用等号,只能用is
select name,post from emp where post_comment = null; #Empty set (0.00 sec)
select name, post from emp where post_comment is null;
select name,post from emp where post_comment is not null;

3、group by 分组

比如: 一张员工表中有性别字段,可以根据性别分组,一组是男性,一组是女性,或者是根据部门分组,有教学部、销售部等...

    - 书写顺序:
		- select 字段1, 字段2...
		- from 表名
		- where 查询条件
        - group by 分组条件

    - 执行顺序:
        - from
        - where
        - group by
        - select


# 严格模式下只能获取分组字段post数据, 无法获取其他字段信息,就好比是进程之间数据隔离,但是可以使用聚合函数来获取
'''
    聚合函数:
        count: 计数
        max: 最大值
        min: 最小值
        avg: 平均值
        sum: 求和

注意: 聚合函数: 
	1、只能跟在group by后(执行顺序)使用  
	2、若查询语句没有group by,则默认整张表就是一个分组。
'''

# 严格模式设置
"""
设置sql_mode为only_full_group_by,意味着以后但凡分组,只能取到分组的依据,
不应该在去取组里面的单个元素的值,那样的话分组就没有意义了,因为不分组就是对单个元素信息的随意获取

show variables like "%mode%";
set global sql_mode="strict_trans_tables,only_full_group_by";

"""

1.按部门分组
select post from emp group by post;


2.获取每个部门的最高工资
select post,max(salary) from emp group by post;
# as: 起别名; 给获取出来的数据字段名,设置别名
select post as '部门', max(salary) as '薪资' from emp group by post;
# 可简写, 但不推荐
select post '部门', max(salary) '薪资' from emp group by post;
# 每个部门的最低工资
select post, min(salary) from emp group by post;
# 每个部门的平均工资
select post, avg(salary) from emp group by post;
# 每个部门的工资总和
select post, sum(salary) from emp group by post;
# 每个部门的人数: count() 中传任意参数都没问题
select post, count(id) from emp group by post;
select post, count(post) from emp group by post;


3.查询分组之后的部门名称和每个部门下所有员工的姓名
# group_concat(name): 不仅可以获取分组后的某一个字段,并且可以对字符串进行拼接
select post,group_concat(name) from emp group by post;
# 给每个部门的员工名字前 + NB_
select post, group_concat('NB_', name) from emp group by post;


4.补充concat(不分组时用)拼接字符串达到更好的显示效果 as语法使用
select concat('Name: ', name) as '名字', concat('Sal: ', salary) as '薪资' from emp;


5.补充as语法 既可以给字段起别名也可以给表起
select emp.name as '名字', emp.salary as '薪资' from emp;


6.求各部门所有员工的年薪
#查询四则运算
select name, salary * 12 as annual_salary from emp;


7.查询岗位名以及岗位包含的所有员工名字
select post,group_concat(name)from emp group by post;


8.查询公司内男员工和女员工的个数
select sex, count(id) from emp group by sex;


9.统计各部门年龄在30岁以上的员工平均工资
#思路步骤:先查找所有年龄30岁以上的员工,再根据部门分组查询它们的平均工资;
select post,avg(salary) from emp where age>30 group by post;

4、having 过滤

   - 书写顺序:
        - select
        - from
        - where
        - group by
        - having

    - 执行顺序:
        - from
        - where
        - group by
        - having
        - select
注意:
    1.having与where语法一样,只不过having需要在group by后使用;
    2.where 不能使用聚合函数,但having可以;

1、统计各部门年龄在30岁以上的员工平均工资,并且保留平均工资大于10000的部门;
select post,avg(salary) from emp where age>30 group by post having avg(salary)>10000;

5、distinct 去重

    - 书写顺序:
        - select
        - distinct
        - from
        - where
        - group by
        - having

    - 执行顺序:
        - from
        - where
        - group by
        - having 过滤的条件
        - distinct
        - select
        
注意: 查询的字段值必须是重复的才有效,只要有一个字段值是不重复的就没有效果。

1.将重复的部门数据去掉
select distinct post, id from emp; #id没有重复的,故过滤不掉
select distinct post from emp;

6、order by 排序

    - 书写顺序:
        - select
        - from
        - where
        - group by
        - having
        - order by

    - 执行顺序:
        - from
        - where
        - group by
        - having
        - select
        - order by #通过select获取的数据进行排序
        
1、根据薪资进行升序
select * from emp order by salary;  # 默认升序
select * from emp order by salary asc;  # 指定升序
select * from emp order by salary desc;  # 指定降序


2、先按照age升序,再按照salary降序
select * from emp order by age asc, salary desc;


3、统计 各部门(分组) 年龄在18岁以上的员工平均工资,并且保留平均工资大于7000的部门,然后对平均工资进行升序
select post,avg(salary) from emp where age>10 group by post having avg(salary)>7000 order by avg(salary);
#按姓名的长度显示员工的姓名和工资
select  *length(last_name) 字节长度,last_name,salary 
from employees 
order by length(last_name)  Desc;

#按照多个字段排序,salary相同时才按employee来排序
select * from employees order by salary asc,employee_id desc;

order by 句子中可以支持单个字段、多个字段、表达式、函数、别名

 

7、limit 限制结果返回数量

应用场景: 类似于博客园首页的数据展示,每一页有固定的数量。

    - 书写顺序:
        - select
        - distinct
        - from
        - where
        - group by
        - having
        - order by
        - limit

	执行顺序:
		- from
		- where
		- group by
		- having
		- select 
		- distinct
		- order by 
		- limit

1、从第一条开始,获取4条记录;
select * from emp limit 4;

2、limit可以有两个参数, 参数1是限制的开始位置+1, 参数2是从开始位置展示的条数;
select * from emp limit 0, 4;
select * from emp limit 4, 4; # 从第五条数据开始查找,获取4条


3、查询工资最高的人的详细信息
select * from emp order by salary limit 1;

8、正则

在编程中,凡是看到reg开头的,基本上都是跟正则有关

select * from emp where name regexp '^程.*(金|银|铜|铁)$';

二、多表查询

多表查询

  • 联表查询
  • 子查询

表数据都存放在硬盘中,存不是目的,目的是为了取,所以我们将数据从硬盘读到内存中,接下来我们应该将他们拼成一张表来查询更加合理。这样可以通过一张表查另一张表的数据;

创建表与插入数据准备

#建表
create table dep2(id int,name varchar(20));

create table emp2(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int);

#插入数据
insert into dep2 values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营');

insert into emp2(name,sex,age,dep_id) values
('tank','male',17,200),
('egon','female',48,201),
('kevin','male',38,201),
('jason','female',28,202),
('owen','male',18,200),
('sean','female',18,204);

1、联表查询

左表的一条记录与右表的一条记录都对应一遍称之为 --> "笛卡尔积" PS: 百度科普

将所有的数据都对应了一遍,虽然不合理但是其中有合理的数据,现在我们需要做的就是找出合理的数据

# 一 比较麻烦的表关联
1、查询员工以及所在部门的信息;
# 将两张表合并,并且根据id字段去判断
select * from emp2, dep2 where emp2.dep_id=dep2.id;


2、查询部门为技术部的员工及部门信息
select * from emp2, dep2 where emp2.dep_id = dep2.id and dep2.name = '技术';


#二 将两张表关联到一起的操作,有专门对应的方法
1、inner join  
内连接:只取两张表有对应关系的记录

select * from emp2 inner join dep2 on emp2.dep_id = dep2.id;
select * from emp2 inner join dep2 on emp2.dep_id = dep2.id and dep2.name = '技术';


2、left join  
左连接: 在内连接的基础上保留左表没有对应关系的记录

select * from emp2 left join dep2 on emp2.dep_id = dep2.id;


3、right join
右连接: 在内连接的基础上保留右表没有对应关系的记录

select * from emp2 right join dep2 on emp2.dep_id = dep2.id;


4、union
全连接:在内连接的基础上保留左、右面表没有对应关系的记录

select * from emp2 left join dep2 on emp2.dep_id = dep2.id
union
select * from emp2 right join dep2 on emp2.dep_id = dep2.id;

2、子查询

子查询就是将一个查询语句的结果用括号括起来,当做另一个查询语句的条件去用

1.查询部门是技术或者人力资源的员工信息
'''
先获取技术部和人力资源的id号,再去员工表里根据前面的id筛选出符合要求的员工信息;
'''
select * from emp2 where dep_id in (select id from dep2 where name='技术' or name='人力资源');


2.每个部门最新入职的员工 
'''
思路:先查每个部门最新入职的员工,再按部门对应上联表查询
'''

select t1.id, t1.name, t1.hire_date, t1.post, t2.* from emp as t1 
inner join 
(select post, max(hire_date) as max_date from emp group by post) as t2 
on t1.post = t2.post
where t1.hire_date = t2.max_date;


'''
as的作用
	- 可以给表起别名
	- 可以给查出来的虚拟表起别名
	- 可以给字段起别名
'''

CREATE TABLE stuinfo(id INT PRIMARY KEY,stuName VARCHAR(20) NOT NULL,sex CHAR(1) CHECK(gender = '男' OR gender ='女'),seat INT UNIQUE,age INT DEFAULT 18,majorId INT  REFERENCES major(id));

posted @ 2020-09-27 09:40  繁星春水  阅读(680)  评论(0编辑  收藏  举报
1 //2 3
//4