欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

mysql 查询所有queryid MySQL 查询所有女生学号

最编程 2024-08-11 17:59:44
...


MySQL 查询题型总结
### MySQL 3 
use `school`;

-- 查询所有学生的所有信息
select * from tb_student;
select  stu_id,
        stu_name,
        stu_addr
   from tb_student;

use school;
-- 查询学生的学号、姓名和籍贯(投影)
select stu_id as 学号,
       stu_name as 姓名,
       stu_addr as 籍贯 
	from tb_student;
-- alias _ 别名
-- 查询所有课程的名称及学分(投影和别名)
select stu_id as '学生编号',
       stu_name as '学生姓名'
  from tb_student;  

-- 查询所有女学生的姓名和出生日期(筛选)
select stu_name,
       stu_birth
  from tb_student
  where stu_sex = 0;

-- 查询籍贯为“四川成都”的女学生的姓名和出生日期(筛选)
select stu_name,
       stu_birth
	from tb_student
    where stu_sex = 0 and
          stu_addr = '四川成都';
          

-- 查询籍贯为“四川成都”或者性别是女的学生
select stu_name,
       stu_birth
	from tb_student
    where stu_sex = 0 or
          stu_addr = '四川成都';

-- 查询所有80后学生的姓名、性别和出生日期(筛选)
select stu_name,
	   stu_sex,
       stu_birth
	from tb_student
    where stu_birth>='1980-1-1' and
          stu_birth<='1990-1-1';
-- 或者使用betwee    and  
-- 可以做数学运算,性别+10,还可以做除法运算,加法,求余数,加减乘除求余运算,写筛选条件也可以写数学运算
select stu_name,
	   stu_sex +10,
       stu_birth
	from tb_student
    where stu_birth between '1980-1-1' and '1990-1-1';

-- 查询学分大于2的课程的名称和学分(筛选)
select cou_name as '课程名称',
       cou_credit as '课程学分'
	from tb_course
    where cou_credit > 2;

-- 查询学分是奇数的课程的名称和学分(筛选)
select cou_name ,
       cou_credit 
	from tb_course
    where cou_credit mod 2 <> 0;       -- 这两种方法都可以的
    -- where cou_credit % 2 <> 0 ;    -- mod 取余

-- 查询选择选了1111的课程考试成绩在90分以上的学生学号(筛选)
select stu_id
 from  tb_record
 where cou_id = 1111 and
       score > 90;

-- 查询名字叫“杨过”的学生的姓名和性别
select stu_name as '学生姓名',
-- case when then else end 分支结构
-- 使用if函数构造分支结构, if 是mysql方言,不建议使用,不适用于其他的数据库
     --  if(stu_sex ,'男','女') as 性别
       case stu_sex  when 1 then '男' when 0 then '女' else '未知' end  as '学生性别'  
  from tb_student
  where stu_name = '杨过';
  
--  ¥¥¥ 复习的时候,练习写的
select stu_name as '学生性别',
	   case stu_sex when 1 then '男' when 0 then '女' else '未知' end as '性别'
	from tb_student
    where stu_name = '杨过';
    
-- 查询姓“杨”的学生姓名和性别(模糊)
-- like是像的意思
-- 通配符 - wild card - 百搭牌
-- % 匹配0个或任意多个字符
-- _ 下划线精确匹配一个字符,如果要匹配两个字符,就写两个下划线
-- 方法1 使用 _ 来匹配,一个_匹配一个字符,两个_ _ 匹配两个字符
select stu_name,
       stu_sex
	from tb_student
    -- where stu_name like '杨%'; 
    where stu_name like '杨_' or
          stu_name like '杨__';   -- 两种方法都可以
-- 方法2 使用%来匹配
select stu_name,
       case stu_sex when 1 then '男' when 0 then '女' else '未知' end  as '性别'
  from tb_student
  where stu_name like '杨%';
  
-- 查询姓“杨”名字两个字的学生姓名和性别(模糊)
select stu_name,
       stu_sex
	from tb_student
    where stu_name like '杨_';
    
-- 查询姓“杨”名字三个字的学生姓名和性别(模糊)
select stu_name,
       stu_sex
	from tb_student
    where stu_name like '杨__';
    
-- 查询名字中有“不”字或“嫣”字的学生的姓名(模糊)
select stu_name,
       stu_sex
	from tb_student
    where stu_name like '%不%' or
		  stu_name like '%嫣%';
          
-- 方法2使用union 连接两个查询,把两个条件分开写。
select stu_name,
       stu_sex
	from tb_student
    where stu_name like '%不%'
union  -- union all是把集合的元素不去重,直接把数据组装起来,而union 是会去重的,
select stu_name,
       stu_sex
	from tb_student
    where stu_name like '%嫣%';
 -- 关系代数-以集合论、一阶谓词逻辑逻辑
 -- mysql没有交,差运算,只有并集运算。
 -- 使用正则表达式进行查询,在必知必会第9章。
 
 -- 查询姓“杨”名字三个字的学生姓名和性别(模糊)
 -- regexp  -  regular expression - 正则表达式
 select  stu_name
     from tb_student
     where stu_name regexp '[杨][\\u4e00-\\u9fa5]{1,2}';  # -- \表示转义,\u表示的是unicode编码。
 
-- 查询姓“杨”或姓“林”名字三个字的学生的姓名(正则表达式模糊查询)
select  stu_name
     from tb_student
     where stu_name regexp '[杨林][\\u4e00-\\u9fa5]{2}';
-- 查询没有录入籍贯的学生姓名(空值处理)  
-- 三值逻辑: true / false / unkown   <-----空值做任何运算的结果,结果仍然是空值,空值做关系运算,逻辑运算,会产生unkown,判断空或者非空,要写is null ,或者is not null。
-- 为空要写 is null ,不为空,写is not null
-- tirm --MYSQL函数- 修剪字符串两端的空格.
select stu_name
  from tb_student
  where stu_addr is null ;  -- sql关系运算,真假未知,
 
update  tb_student set stu_addr  = '' where stu_id = 3755;
-- 查询录入了籍贯的学生姓名(空值处理)
select stu_name
  from tb_student
  where stu_addr is not  null ; 
-- 如果字符串是空字符串
-- 下面查询没有籍贯的学生,包括字符串的值为空
select stu_name
  from tb_student
  where stu_addr is null or 
        stu_addr = '';   #可以查到两个学生记录

-- 下面查询有籍贯的学生,不包括字符串的值为空
select stu_name
  from tb_student
  where stu_addr is  not null and
        stu_addr <> ''; 
-- 如果查询的数据中,字符串是很多空格,以上方法任然查不出来。,要加函数trim(),如果要去掉左边的空格或数据,就使用leleading'什么内容'
-- 德摩根律
update tb_student set stu_addr = '   ' where stu_id = 3755;
-- 找地址空的
select stu_name
  from tb_student
  where stu_addr is null or 
        trim(stu_addr) = ''; 
-- 找地址非空
select stu_name
  from tb_student
  where stu_addr is not null and
        trim(stu_addr) <> ''; 
-- 查询学生选课的所有日期(去重)
-- distinct 去重
select distinct sel_date
  from tb_record;
  
-- 查询学生的籍贯(去重)
select distinct sel_date
  from tb_record
  where stu_addr is not null and
        trim(stu_addr) <>'';
-- 查询男学生的姓名和生日按年龄从大到小排列(排序)
-- asc ascending  -上升 - 从小到大
-- desc -- descending  - 下降 - 从大到小
select stu_name,
       stu_birth
	from tb_student
    where stu_sex = 1
	order by stu_birth asc, stu_id desc;
-- 时间日期函数
-- now() - 获取当前数据库服务器的时间
-- curdate() - current date - 获取当前日期
-- curtime()  --current time - 获取当前时间
-- datediff(dt1,dt2)
-- 数数值函数
-- floor() - 向下取整
--  ceiling() /ceil() - 向上取整
-- round()- 四舍五入
select now();  -- 当前时间日期
select curdate();  -- 当前日期
select curtime();  -- 当前时间
-- 补充:将上面的生日换算成年龄(日期函数、数值函数)
select stu_birth,
	   stu_name,
       floor(datediff(curdate(),stu_birth)/365) as '年龄'
  from tb_student
  where stu_sex = 1 order by 年龄 desc;
help functions;
help time;
-- 查询年龄最大的学生的出生日期(聚合函数) - 查询最小的生日
select min(stu_birth)
   from tb_student;
   

-- 查询年龄最小的学生的出生日期(聚合函数)  - 查询年龄最小的
select max(stu_birth)
   from tb_student;
-- 查询编号为1111的课程考试成绩的最高分(聚合函数)
select max(score)
   from tb_record
   where cou_id = 1111;   -- 95.0
-- 查询学号为1001的学生考试成绩的最低分(聚合函数)
select min(score)
   from tb_record
   where stu_id = 1001;  -- 87.5
-- 查询学号为1001的学生考试成绩的平均分(聚合函数)
-- 注意:聚合函数不会处理空值(null),即求和,计数不会考虑空值的。
-- 聚合函数有max/min/avg/sum/count
-- count(*) 统计所有的行数,count(score)统计所有的非空值
-- coalesce(x,y,z)返回参数中第一个非空值。
select round(avg(score),1)  -- 95.6
   from tb_record
   where stu_id = 1001;  -- 95.62500 --想要保留几位小数,使用round函数。
   
   
-- 查询学号为1001的学生考试成绩的平均分,如果有null值,null值算0分(聚合函数)
select round(avg(score),1)  -- 95.6
   from tb_record
   where stu_id = 1001;  -- 95.62500
   
-- 方法1
select sum(score)/count(*) 
   from tb_record
   where stu_id = 1001;  -- 76.50000
-- 方法2 ifnull --MYSQL函数,如果score是空值,就返回0,否则就是返回score.
select avg(ifnull(score,0)) 
   from tb_record
   where stu_id = 1001;  -- 76.5
-- 方法3 使用 coalesce  
select avg(coalesce(score,0))   -- 通用的
   from tb_record
   where stu_id = 1001;   -- 76.50000 
-- 方法4 
select avg(case when score is null then 0 else score end)
   from tb_record
   where stu_id = 1001;  -- 76.5000
-- Hao(jackfrued) 14:09:05
-- 获取描述性统计信息
-- 均值、中位数、分位数、最大值、最小值、极差(全距)、方差、标准差
-- 均值在数据大小比较几种的时候,有很好的代表性
-- 方差 :给误差(每个数据跟均值的差)的平方求平均值。
-- 总体方差:
-- 样本方差:
-- 方差越大,均值的代表性越弱,方差越小,均值的代表性越好。
-- 查询学号为1001的学生考试成绩的标准差(聚合函数)
-- MYSQL函数  - std
-- oracle函数 -  stddev
-- 标准SQL
-- 标准差SQL - stddev_pop / sttdev_samp  总体标准差与样本标准差
-- 标准方差SQL - var_pop / var_samp  总体方差与样本方差
-- population - 总体 / sample - 样本
-- MYSQL方言 - std 
-- 
select variance(score)
   from tb_record
   where stu_id = 1001;  -- 方差 26.17
   
select round(stddev(score),2)
   from tb_record
   where stu_id = 1001;  -- 标准差 5.12
select round(stddev(score),2)
   from tb_record
   where stu_id = 1002;    -- 11.5
-- 举例说明 样本方差和标准差的计算
select stddev_samp(score)
   from tb_record
   where stu_id = 1002;   -- 样本标准差16.263455967290593

select var_samp(score)
   from tb_record
   where stu_id = 1002;   -- 样本方差 264.5

select stddev_pop(score)
   from tb_record
   where stu_id = 1002;   -- 总体标准差11.5

select var_pop(score)
   from tb_record
   where stu_id = 1002;   -- 总体方差132.25
help functions;  --查函数
help 'Aggregate Functions and Modifiers';  -- 查聚合函数

-- 查询男女学生的人数(分组和聚合函数) - 根据性别统计人数 
-- 要学会在不同的维度拆解数据
select case stu_sex when 1 then '男'else '女' end as 性别,

         count(*) as 人数
	 from tb_student
     group by stu_sex;


-- 查询每个学院学生人数(分组和聚合函数)
select col_id as 学院,
count(*) as 人数
  from tb_student
  group by col_id;
  
-- 查询每个学院男女学生人数(分组和聚合函数)
select col_id as 学院编号, -- 别名不需要单引号
       case stu_sex when 1 then '男' else '女' end as 性别,
       count(*) as 人数
  from tb_student
  group by col_id,stu_sex;
-- 查询每个学生的学号和平均成绩(分组和聚合函数)
select stu_id as 学生学号,
		round(avg(score),1) as 平均分
  from tb_record
  group by stu_id;  
-- 听过学号分组,然后计算平均分
-- 查询平均成绩大于等于90分的学生的学号和平均成绩
select stu_id,
       avg(score) as '平均成绩'
	from tb_record
    group by stu_id
    having avg(score) >= 90; 
     
-- 查询1111、2222、3333三门课程平均成绩大于等于90分的学生的学号和平均成绩
select stu_id as 学号,
       cou_id as  课程编号,
       round(avg(score),1) as 平均成绩
	from tb_record
    where cou_id in (1111,2222,3333)
    group by stu_id
    having avg(score) >= 90;   

-- 查询年龄最大的学生的姓名(子查询/嵌套查询)
-- 以前的方法
select min(stu_birth)  -- 赋值不能用等号,等号表示相等,判断的,赋值要用海象运算符
  from tb_student;  -- 1985-04-17
-- 新的方法 
select @x := min(stu_birth)  -- 赋值不能用等号,等号表示相等,判断的,赋值要用海象运算符
  from tb_student;
select @x ;  -- 把值赋值给了@X
select stu_name
   from tb_student
   where stu_birth = @x;  -- 杨不悔
-- 方法2 subquery - 把一个查询结果作为另一个查询的一部分来使用!!! 
select stu_name
   from tb_student
   where stu_birth = (select min(stu_birth)  
                                 from tb_student);  -- 杨不悔
 
-- 查询选了两门以上的课程的学生姓名(子查询/分组条件/集合运算)
select stu_name
  from tb_student
  where stu_id in (select stu_id   
                          from tb_record 
                          group by stu_id
                          having count(*) > 2);

-- 学号在这个集合里面就要去把数据拿出来。也可以写成一下方法
-- any 是一个谓词,可以产生真和假 ,作用是只要和后面任意一个匹配上,就返回真。
-- all 是一个谓词,----> 所有的元素都匹配成功才返回true.
-- in 集合运算也称为一个谓词。
-- 自己理解的 any是只要匹配上,就会返回值,然后会把集合里面每一个都元素都进行匹配?
select stu_name
  from tb_student
  where stu_id = any (select stu_id   
                          from tb_record 
                          group by stu_id
                          having count(*) > 2);
%*
杨过
王语嫣
项少龙
*%
-- 查询学生的姓名、生日和所在学院名称 (连接/表查询--- 从多张表投影字段)

-- from 子句如果放多张表但是没有连接记录的条件就会形成一个笛卡尔积,
-- 通过连接条件可以多张表满足条件的记录连接起来
-- python -- > 包名.模块名.函数名 ---> 完全限定名
-- 列,表都可以别名 as 可以省略

-- from  a  import foo
-- from b import foo
-- import a
-- import b
-- a.foo
-- b.foo
select  stu_name,
        stu_birth,
        col_name
	-- from tb_student as t1,tb_college as t2  -- 给两张表换个名字
    -- where t1.col_id = t2.col_id;
    from tb_student as t1,tb_college as t2 
    where tb_student.col_id = tb_college.col_id;
 -- 或者使用 from 表1 iner join 表名2 on 条件   ;
 select  stu_name,
        stu_birth,
        col_name
    from tb_student inner join tb_college on tb_student.col_id = tb_college.col_id ;
 
    
-- 查询学生姓名、课程名称以及成绩(连接查询/联结查询)
select stu_name,
	   cou_name,
       score
	from tb_student as t1,tb_course as t2,tb_record as t3
	where t1.stu_id = t3.stu_id and
          t2.cou_id = t3.cou_id and
          score is not null
  -- 方法2 内连接  使用 inner join  on 
  -- Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'nul' at line 5

 select stu_name,
	    cou_name,
        score
	from tb_student inner join tb_record on tb_student.stu_id = tb_record.stu_id inner join tb_course  on tb_course.cou_id = tb_record.cou_id
	where  score is not null ;
  -- 注意: 写时候on 后面的连接条件的时候,一定要注意对应关系。
  
  -- 方法3 自然连接 表1 natural join 表2 ,如果有同名列,就会连接,如果没有同名列,就会产生笛卡尔积。
  select stu_name,
	     cou_name,
         score
	from tb_student natural join tb_course natural join tb_record 
	where  score is not null;
  
  
-- 补充:上面的查询结果取前5条数据(分页查询)

-- limit 数字 只拿前几条
-- offset 数字  --- 跳过指定数字个 ,分页数据
 select stu_name,
	     cou_name,
         score
	from tb_student natural join tb_course natural join tb_record 
	where  score is not null
    order by cou_id asc ,score desc
    limit 5
    offset 10; 
 -- limit 数字1,数字2   --- 跳过数字1个数据,拿数字2个数据   。
 -- 方法2 使用limit 数字1,数字2
 select stu_name,
	     cou_name,
         score
	from tb_student natural join tb_course natural join tb_record 
	where  score is not null
    order by cou_id asc ,score desc
    limit 10 , 5;
    
-- 补充:上面的查询结果取第6-10条数据(分页查询)
use school;

 select stu_name,
	     cou_name,
         score
	from tb_student natural join tb_course natural join tb_record 
	where  score is not null
    order by cou_id asc ,score desc
    limit 5 , 5;
-- 补充:上面的查询结果取第11-15条数据(分页查询)
 select stu_name,
	     cou_name,
         score
	from tb_student natural join tb_course natural join tb_record 
	where  score is not null
    order by cou_id asc ,score desc
    limit 10 , 5;
    
-- 查询选课学生的姓名和平均成绩(子查询和连接查询)
select stu_name ,
       avg_score
	from tb_student natural join (select stu_id,
									  round(avg(score),1) as avg_score
                                      from tb_record

                                      group by stu_id) as tb_ave;
select stu_name as 学生姓名,
       round(avg(score),1) as 平均成绩
	from tb_student natural join  tb_record
    group by stu_id;


-- 查询学生的姓名和选课的数量 (只能查到选了课的学生的姓名和数量,还有三个没有选课的学生,不会查到)
-- 方式1 使用两个表的自然连接,是通过学号
select stu_name as 学生姓名,
       count(*) as 选课数量
	from tb_student natural join  tb_record
    group by stu_id;
    
 -- 方式2   使用内连接
 select stu_name as 学生姓名,
            count as 选课数量
	from tb_student inner join  select(
    group by stu_id;
 
-- 查询每个学生的姓名和选课数量(左外连接和子查询)

-- 内连接/自然连接:只会将两张表满足连表条件的记录取到
-- 左外连接:获取左表(写在join前面的表)所有的数据,不满足连表条件的地方填充null
-- 写法:left outer join  /left join
-- 右外连接:获取右表(写在join后面的表)所有的数据,不满足连表条件的地方填充null
-- 写法:rignt outer join  /rignt join
-- 全外连接:获取左表和右表所有数据,不满足连表条件的地方填充null (mysql不支持全外连接)
-- 写法:full outer join  /full join
-- 在MYsql中如果要实现全外连接的效果,可以用左外连接和右外连接求并集
select  stu_name,
        count(rec_id) as 选课数量
	from tb_student  left outer join tb_record on tb_student.stu_id = tb_record.stu_id
	group by tb_student.stu_id;
    
select stu_name,
       count(rec_id) as total
	from tb_student left join (select )

-- 查询没有选课的学生的姓名
-- 方法一、去重加集合运算,集合运算 去重操作,性能比较低,
select stu_name
   from tb_student
   where stu_id not in (select distinct stu_id
								   from tb_record);
-- 方法2 按照两个集合的差集方法,两个表进行左连接,然后,右表的主键是空集
select stu_name
   from tb_student left join tb_record
        on tb_record.stu_id = tb_student.stu_id
   where  rec_id is null;


#### 练习提升题

-- 01. 查询课程1111比课程2222成绩高的所有学生的学号
select 
	sid1
from 
	(select stu_id as sid1, score from tb_record where cou_id=1111) as t1
inner join 
	(select stu_id as sid2, score from tb_record where cou_id=2222) as t2
on sid1=sid2 
where t1.score>t2.score;

-- 02. 查询没有学过张三丰老师的课程的同学的学号和姓名
select stu_id, stu_name 
from tb_student 
where stu_id not in (
	select stu_id from tb_teacher 
    natural join tb_course 
    natural join tb_record
    where tea_name='张三丰'
);

select stu_id, stu_name 
from tb_student as t1 
where not exists (
	select 'x' from tb_teacher 
    natural join tb_course 
    natural join tb_record as t2
    where t1.stu_id=t2.stu_id and tea_name='张三丰'
);

-- 03. 查询学过课程1111和课程2222的同学的学号和姓名
select stu_id, stu_name 
from tb_student 
where stu_id in (
	select t1.stu_id 
	from tb_record t1
	inner join tb_record t2 
    on t1.stu_id=t2.stu_id
	where t1.cou_id=1111 and t2.cou_id=2222
);

-- 04. 查询所有课程成绩大于80分的同学的学号和姓名
select stu_id, stu_name 
from tb_student 
where stu_id in (
	select stu_id
	from tb_record
	group by stu_id
	having min(score)>80
);

-- 05. 查询学习了所有课程的同学的学号和姓名
select stu_id, stu_name 
from tb_student 
where stu_id in (
	select stu_id 
	from tb_record 
	group by stu_id
	having count(cou_id)=(
		select count(cou_id) from tb_course
	)
);

-- 06. 查询至少有一门课跟学号1001同学所学课程相同的同学的学号和姓名
select stu_id, stu_name 
from tb_student 
where stu_id in (
	select distinct stu_id from tb_record 
	where cou_id in (
		select cou_id from tb_record where stu_id=1001
	) and stu_id<>1001
);

-- 07. 查询所学课程与1002同学完全相同的同学的学号和姓名

-- 08. 查询有课程不及格(60分以下)的同学的学号和姓名

-- 09. 查询每门课程的名称、最高分、最低分和平均分

-- 10. 查询每门课程第2名和第3名的学生姓名和对应的成绩

-- 11. 查询每门课程各分数段(100-90,90-80,80-70,70-60,60以下)的人数占比
select 
	cou_id,
    sum(if(score>=90 and score<=100, 1, 0))/count(score) as A,
    sum(if(score>=80 and score<90, 1, 0))/count(score) as B,
    sum(if(score>=70 and score<80, 1, 0))/count(score) as C,
    sum(if(score>=60 and score<70, 1, 0))/count(score) as D,
    sum(if(score<60, 1, 0))/count(score) as E
from tb_record
group by cou_id;

-- 12. 查询本周过生日的学生的姓名和生日(考察对日期函数的熟练程度)
select stu_name, stu_birth 
from tb_student 
where weekofyear(stu_birth)=weekofyear(curdate());
-- where weekofyear(stu_birth)+1=weekofyear(curdate());  -- 上周过生日
-- where weekofyear(stu_birth)=weekofyear(curdate())+1; -- 下一周过生日



-- 补充日期函数中日期格式化---date_format(数据,‘转换的格式’)

-- 1.日期格式化函数
-- date_format()

-- %a 缩写星期名
-- %b 缩写月名
-- %c 月,数值
-- %D 带有英文前缀的月中的天
-- %d 月的天,数值(00-31)
-- %e 月的天,数值(0-31)
-- %f 微秒
-- %H 小时 (00-23)
-- %h 小时 (01-12)
-- %I 小时 (01-12)
-- %i 分钟,数值(00-59)
-- %j 年的天 (001-366)
-- %k 小时 (0-23)
-- %l 小时 (1-12)
-- %M 月名
-- %m 月,数值(00-12)
-- %p AM 或 PM
-- %r 时间,12-小时(hh:mm:ss AM 或 PM)
-- %S 秒(00-59)
-- %s 秒(00-59)
-- %T 时间, 24-小时 (hh:mm:ss)
-- %U 周 (00-53) 星期日是一周的第一天
-- %u 周 (00-53) 星期一是一周的第一天
-- %V 周 (01-53) 星期日是一周的第一天,与 %X 使用
-- %v 周 (01-53) 星期一是一周的第一天,与 %x 使用
-- %W 星期名
-- %w 周的天 (0=星期日, 6=星期六)
-- %X 年,其中的星期日是周的第一天,4 位,与 %V 使用
-- %x 年,其中的星期一是周的第一天,4 位,与 %v 使用
-- %Y 年,4 位
-- %y 年,2 位

SELECT  DATE_FORMAT(day,'%W, %M %e, %Y')  AS day 
  FROM Days;
  
-- 问题 {"headers":{"Days":["day"]},"rows":{"Days":[["2022-04-12"],["2021-08-09"],["2020-06-26"]]}}
-- 结果 {"headers": ["day"], "values": [["Tuesday, April 12, 2022"], ["Monday, August 9, 2021"], ["Friday, June 26, 2020"]]}



### MySQL  4  第几前几——---——窗口函数

-- 如果存在就删除名为hrs的数据库
drop database if exists `hrs`;

-- 创建名为hrs的数据库并指定默认的字符集
create database `hrs` default charset utf8mb4;

-- 切换到hrs数据库
use `hrs`;

-- 创建部门表
create table `tb_dept`
(
`dno` int not null comment '编号',
`dname` varchar(10) not null comment '名称',
`dloc` varchar(20) not null comment '所在地',
primary key (`dno`)
);

-- 插入4个部门
insert into `tb_dept` values 
    (10, '会计部', '北京'),
    (20, '研发部', '成都'),
    (30, '销售部', '重庆'),
    (40, '运维部', '深圳');

-- 创建员工表
create table `tb_emp`
(
`eno` int not null comment '员工编号',
`ename` varchar(20) not null comment '员工姓名',
`job` varchar(20) not null comment '员工职位',
`mgr` int comment '主管编号',
`sal` int not null comment '员工月薪',
`comm` int comment '每月补贴',
`dno` int not null comment '所在部门编号',
primary key (`eno`),
constraint `fk_emp_mgr` foreign key (`mgr`) references tb_emp (`eno`),
constraint `fk_emp_dno` foreign key (`dno`) references tb_dept (`dno`)
);

-- 插入14个员工
insert into `tb_emp` values 
    (7800, '张三丰', '总裁', null, 9000, 1200, 20),
    (2056, '乔峰', '分析师', 7800, 5000, 1500, 20),
    (3088, '李莫愁', '设计师', 2056, 3500, 800, 20),
    (3211, '张无忌', '程序员', 2056, 3200, null, 20),
    (3233, '丘处机', '程序员', 2056, 3400, null, 20),
    (3251, '张翠山', '程序员', 2056, 4000, null, 20),
    (5566, '宋远桥', '会计师', 7800, 4000, 1000, 10),
    (5234, '郭靖', '出纳', 5566, 2000, null, 10),
    (3344, '黄蓉', '销售主管', 7800, 3000, 800, 30),
    (1359, '胡一刀', '销售员', 3344, 1800, 200, 30),
    (4466, '苗人凤', '销售员', 3344, 2500, null, 30),
    (3244, '欧阳锋', '程序员', 3088, 3200, null, 20),
    (3577, '杨过', '会计', 5566, 2200, null, 10),
    (3588, '朱九真', '会计', 5566, 2500, null, 10);

-- 查询员工及其主管的姓名


insert into tb_emp values 
	(8899, '骆昊', '分析师', 7800, 9000, 5000, 20);

-- 查询月薪最高的员工姓名和月薪 ---> 至少给出3种写法
-- 方法一:排序法(不推荐)
select ename,
       sal
  from tb_emp
 order by sal desc
  limit 1;

-- 方法二:嵌套查询
select ename,
       sal
  from tb_emp
 where sal = (select max(sal)
                from tb_emp);

-- 方法三:all / any
select ename,
       sal
  from tb_emp
 where sal >= all(select sal
                    from tb_emp);
                    
-- 方法四:计数法
-- 推广到找工资的第几名或者工资的前几名
select ename,
       sal
  from tb_emp as t1
 where (select count(*)
          from tb_emp as t2
		 where t2.sal > t1.sal) = 0;

-- 方法五:存在性判断
select ename,
       sal
  from tb_emp as t1
 where not exists (select 'x'
                     from tb_emp as t2
					where t2.sal > t1.sal);

-- 查询月薪前3名的员工的姓名和月薪
select ename,
       sal
  from tb_emp as t1
 where (select count(*)
          from tb_emp as t2
		 where t2.sal > t1.sal) < 3
 order by sal desc;

-- 查询员工的姓名和年薪(年薪=(sal+comm)*13)
select ename,
	   (sal + coalesce(comm, 0)) * 13 as ann_sal
  from tb_emp;

-- 查询部门的编号和人数
select dno,
       count(*) as total
  from tb_emp
 group by dno;

-- 查询部门人数超过5个人的部门的编号和人数
select dno,
       count(*) as total
  from tb_emp
 group by dno
having total > 5;

-- 查询所有部门的名称和人数
select dname,
       count(eno) as total
  from tb_dept left join tb_emp
       on tb_dept.dno = tb_emp.dno
 group by tb_dept.dno;

select dname,
	   coalesce(total, 0) as total
  from tb_dept left join (select dno,
							     count(*) as total
						    from tb_emp
						   group by dno) as t
	   on tb_dept.dno = t.dno;

-- 查询月薪超过平均月薪的员工的姓名和月薪
select ename,
       sal
  from tb_emp
 where sal > (select avg(sal) 
                from tb_emp);

-- 查询月薪超过其所在部门平均月薪的员工的姓名、部门编号和月薪
-- Error Code: 1052. Column 'dno' in field list is ambiguous
select ename,
       dno,
	   sal
  from tb_emp as t1
       natural join (select dno,
						    avg(sal) as avg_sal
					   from tb_emp
					  group by dno) as t2
 where sal > avg_sal;

-- 查询部门中月薪最高的人姓名、月薪和所在部门名称
select ename,
       sal,
       dname
  from tb_emp natural join tb_dept
 where (dno, sal) in (select dno,
						     max(sal) as max_sal
					    from tb_emp
					   group by dno);

select ename,
       sal,
	   dname
  from tb_emp natural join tb_dept 
       natural join (select dno,
						    max(sal) as max_sal
					   from tb_emp
					  group by dno) as tmp
 where sal = max_sal;
 
select ename,
       sal,
	   dname
  from tb_emp inner join tb_dept
           on tb_emp.dno = tb_dept.dno
	   inner join (select dno,
						  max(sal) as max_sal
				     from tb_emp
				    group by dno) as tmp
		   on tb_emp.dno = tmp.dno
 where sal = max_sal;

-- 查询主管的姓名和职位
select ename,
       job
  from tb_emp as t1
 where exists (select 'x'
                 from tb_emp as t2
				where t1.eno = t2.mgr);

select distinct t1.ename,
       t1.job
  from tb_emp as t1 inner join tb_emp as t2
       on t1.eno = t2.mgr;

select ename,
       job
  from tb_emp
 where eno in (select distinct mgr
                 from tb_emp
				where mgr is not null);

-- 查询普通员工的姓名和职位
select ename,
       job
  from tb_emp as t1
 where not exists (select 'x'
                     from tb_emp as t2
				    where t1.eno = t2.mgr);

select ename,
       job
  from tb_emp
 where eno not in (select distinct mgr
                     from tb_emp
				    where mgr is not null);

-- 查询主管和普通员工的平均月薪
select tag,
       avg(sal) as avg_sal
  from (select sal,
			   case when exists (select 'x'
								   from tb_emp as t2
								  where t1.eno = t2.mgr)
					then '主管'
					else '普通员工'
			   end as tag
		  from tb_emp as t1) as tmp
 group by tag;

-- 查询月薪排名4~6名的员工排名、姓名和月薪
select t1.*
  from (select @x := @x + 1 as r,
			   ename,
			   sal
		  from tb_emp
		 order by sal desc) as t1, (select @x := 0) as t2
 where r between 4 and 6;
 
-- 窗口函数
-- 排名函数:rank() / dense_rank() / row_number()
select r1,
       ename,
       sal
  from (select ename,
			   sal,
			   rank() over (order by sal desc) as r1,
			   dense_rank() over (order by sal desc) as r2,
			   row_number() over (order by sal desc) as r3
		  from tb_emp) as tmp
 where r1 between 4 and 6;

-- 查询每个部门月薪排前2名的员工姓名、月薪和部门编号
select ename,
       sal,
       dno
  from tb_emp as t1
 where (select count(*)
          from tb_emp as t2
		 where t1.dno = t2.dno 
               and t2.sal > t1.sal) < 2
 order by dno asc, sal desc;

-- 窗口函数
select ename,
       sal,
	   dno
  from (select ename,
			   sal,
			   dno,
			   rank() over (partition by dno order by sal desc) as r
		  from tb_emp) as tmp
 where r <= 2;
 
 
 #### 练习提升题
 create database `demo1` default charset utf8mb4;

use `demo1`;

drop table if exists `tb_order`;

create table `tb_order` (
`id` int not null,
`order_no` varchar(20) not null comment '订单编号',
`user_id` varchar(50) not null comment '用户ID',
`order_date` date not null comment '下单日期',
`store` varchar(50) not null comment '店铺ID',
`product` varchar(50) not null comment '商品ID',
`quantity` int not null comment '购买数量',
primary key (`id`)
) engine=innodb comment='订单表';

lock tables `tb_order` write;

insert into `tb_order` values 
    (1,'d001','customera','2018-01-01','storea','proda',1),
    (2,'d001','customera','2018-01-01','storea','prodb',1),
    (3,'d001','customera','2018-01-01','storea','prodc',1),
    (4,'d002','customerb','2018-01-12','storeb','prodb',1),
    (5,'d002','customerb','2018-01-12','storeb','prodd',1),
    (6,'d003','customerc','2018-01-12','storec','prodb',1),
    (7,'d003','customerc','2018-01-12','storec','prodc',1),
    (8,'d003','customerc','2018-01-12','storec','prodd',1),
    (9,'d004','customera','2018-01-01','stored','prodd',2),
    (10,'d005','customerb','2018-01-23','storeb','proda',1);

unlock tables;

drop table if exists `tb_product`;

create table `tb_product` (
`prod_id` varchar(50) not null comment '商品ID',
`category` varchar(50) not null comment '种类',
`color` varchar(10) not null comment '颜色',
`weight` decimal(10,2) not null comment '重量',
`price` int not null comment '价格',
primary key (`prod_id`)
) engine=innodb comment='商品表';

lock tables `tb_product` write;

insert into `tb_product` values 
    ('proda','catea','yellow',5.60,100),
    ('prodb','cateb','red',3.70,200),
    ('prodc','catec','blue',10.30,300),
    ('prodd','cated','black',7.80,400);

unlock tables;

drop table if exists `tb_store`;

create table `tb_store` (
`store_id` varchar(50) not null comment '店铺ID',
`city` varchar(20) not null comment '城市',
primary key (`store_id`)
) engine=innodb comment='店铺表';

lock tables `tb_store` write;

insert into `tb_store` values 
    ('storea','citya'),
    ('storeb','citya'),
    ('storec','cityb'),
    ('stored','cityc'),
    ('storee','cityd'),
    ('storef','cityb');

unlock tables;

-- 查出购买总金额不低于800的用户的总购买金额,总订单数和总购买商品数,订单号相同的算作一单。
select  sum(quantity*price) as '总购买金额',
        count(distinct order_no) as '总订单数',
        count(distinct product) as '总购买数'
	from tb_order inner join tb_product 
                  on tb_order.product = tb_product.prod_id
    group by user_id
    having sum(quantity*price) >= 800;
    
-- 查出所有城市(包含无购买记录的城市)的总店铺数,总购买人数和总购买金额。
select city as 城市,
       count(distinct store_id) as 总店铺数,
	   count(distinct user_id) as 总购买人数,
       sum(quantity*price) as 总购买金额
	from tb_store left join tb_order 
                   on tb_store.store_id = tb_order.store
                   left join tb_product
                   on tb_order.product = tb_product.prod_id
	group by city; 
-- 查出购买过"catea"产品的用户和他们的平均订单金额,订单号相同的算作一单。
select user_id as '用户名',
	   sum( quantity*price) / count(distinct order_no ) as '平均订单金额'
	from tb_order inner join tb_product
                  on tb_order.product = tb_product.prod_id
	where  category = 'catea'
    group by user_id;
    
### MySQL 5 留存率与选课系统查询

-- 创建用户登录日志表
create database homework ;
use homework;
create table `tb_user_login`
(
`user_id` varchar(300) comment '用户ID',
`login_date` date comment '登录日期'
) engine=innodb comment='登录日志表';

-- 插入数据
insert into `tb_user_login` (`user_id` , `login_date`)
values
    ('A', '2019/9/2'),
    ('A', '2019/9/3'),
    ('A', '2019/9/4'),
    ('B', '2019/9/2'),
    ('B', '2019/9/4'),
    ('B', '2019/9/10'),
    ('C', '2019/1/1'),
    ('C', '2019/1/2'),
    ('C', '2019/1/30'),
    ('C', '2019/9/3'),
    ('C', '2019/9/4'),
    ('C', '2019/9/5'),
    ('C', '2019/9/11'),
    ('C', '2019/9/12'),
    ('C', '2019/9/13');

-- 任务1:查询连续3天登录的用户ID。
-- 方法1 
use homework;
Select distinct user_id
    from tb_user_login  
    where  (user_id ,date_add(login_date,interval 1 day)) in (select * from tb_user_login) 
    and (user_id ,date_add(login_date,interval 2 day)) in (select * from tb_user_login);

-- 方法2 
select distinct `user_id`
from (
    select 
        `user_id`, `login_date`,
        dense_rank() over (
            partition by `user_id` order by `login_date`
        ) as `rn`
    from (
		select `user_id`, `login_date`
        from `tb_user_login`
        group by `user_id`, `login_date`
	) as temp
) as temp 
group by `user_id`, subdate(`login_date`, `rn`)
having count(*) >= 3;

-- 任务2:查询每天新增用户数以及他们次日留存率和三十日留存率。
-- 查询每天新增用户数以及他们次日留存率和三十日留存率。
-- 方法1:
select 
    `first_login`
    , count(distinct t1.`user_id`) as 新增用户数
    , count(distinct t2.`user_id`) / count(distinct t1.`user_id`) as 次日留存率
    , count(distinct t3.`user_id`) / count(distinct t1.`user_id`) as 三十日留存率
from (
    select 
        `user_id`, `login_date`,
        min(`login_date`) over (
            partition by `user_id` order by `login_date`
        ) as `first_login` 
    from `tb_user_login`
) as t1 
left join `tb_user_login` as t2 
on t1.`user_id`=t2.`user_id` and datediff(`first_login`, t2.`login_date`)=-1 
left join `tb_user_login` as t3 
on t1.`user_id`=t3.`user_id` and datediff(`first_login`, t3.`login_date`)=-29
group by `first_login`;

-- 方法2:
select 
    `first_login`,
    count(distinct `user_id`) as 新增用户数,
    sum(case datediff(`login_date`, `first_login`) when 1 then 1 else 0 end) / count(distinct `user_id`) as 次日留存率,
    sum(case datediff(`login_date`, `first_login`) when 29 then 1 else 0 end) / count(distinct `user_id`) as 三十日留存率
from (
    select 
        `user_id`, `login_date`,
        min(`login_date`) over (partition by `user_id` order by `login_date`) as `first_login` 
    from `tb_user_login`
) as t1 
group by `first_login`;


#### Practice
-- 第一题
use homework;
create table `tb_result` (
  `rq` date not null,
  `sf` char(1) not null
) engine=innodb;

insert into `tb_result` values 
    ('2017-04-09','胜'),
    ('2017-04-09','胜'),
    ('2017-04-09','负'),
    ('2017-04-09','负'),
    ('2017-04-10','胜'),
    ('2017-04-10','负'),
    ('2017-04-10','胜');

-- 用上面的表查询出如下所示的结果。
-- | date       | win  | los  |
-- | ---------- | ---- | ---- |
-- | 2017-04-09 | 2    | 2    |
-- | 2017-04-10 | 2    | 1    |

-- 方案1
select distinct rq as date,
	 sum(if(sf = '胜',1,0)) as  win,
     sum(if(sf = '负',1,0)) as  los
	from tb_result
    group by rq;   
 -- 方案2   
 select distinct rq as date,
      sum( case sf when '胜' then  1  else 0   end ) as  win,
	  sum( case sf when '负' then  1  else 0   end ) as  los
	from tb_result
    group by rq;    


-- 第二题
create table `tb_course`
(
`course_id` int unsigned not null comment '课程编号',
`course_name` varchar(50) not null comment '课程名称',
primary key (`course_id`)
) engine=innodb;

insert into `tb_course` values 
    (1, 'Python'),
    (2, 'Java'),
    (3, 'Database'),
    (4, 'JavaScript');

create table `tb_open_date`
(
`month` char(6) not null comment '开课日期',
`course_id` int unsigned not null comment '课程编号'
) engine=innodb;

insert into `tb_open_date` values
    ('202106', 1),
    ('202106', 3),
    ('202106', 4),
    ('202107', 4),
    ('202108', 2),
    ('202108', 4);

-- 用上面的表查询出如下所示的结果。
-- | course_name | Jun | Jul | Aug |
-- | ----------- | --- | --- | --- |
-- | Python      |  o  |  x  |  x  |
-- | Java        |  x  |  x  |  o  |
-- | Database    |  o  |  x  |  x  |
-- | JavaScript  |  o  |  o  |  o  |
select  t1.course_name ,
	case  right(month,1)  when  '6'     then  'o'  else   'X'  end as Jun,
	case  right(month,1)  when  '7'     then  'o'  else   'X'  end as Jul,
   case  right(month,1)  when  '8'     then  'o'  else   'X'  end as Aug
  from  tb_course as t1 inner join   tb_open_date  as t2  on  t1.course_id = t2.course_id 
  -- group by course_name
  order by  t1.course_id asc;




-- 第三题
create table `tb_table1`
(
`userid` int unsigned not null,
`username` varchar(20) not null,
primary key (`userid`)
);

insert into `tb_table1` values
	(1, '张三'),
    (2, '李四'),
    (3, '王五'),
    (4, '赵大'),
    (5, '孙二');

create table `tb_table2`
(
`month` varchar(20) not null,
`userid` int unsigned not null,
`ye` int not null
);

drop table `tb_table2`;
insert into `tb_table2` values
	('一月份', 1, 10),
    ('一月份', 2, 10),
    ('一月份', 3, 5),
    ('一月份', 1, 5),
    ('二月份', 2, 8),
    ('二月份', 2, 7),
    ('二月份', 4, 9),
    ('三月份', 3, 8),
    ('三月份', 5, 6);
    


-- 用上面的tb_table1和tb_table2查询出如下所示的结果。
-- +----------+-----------+-----------+-----------+
-- | 用户名    | 一月份     | 二月份     | 三月份     |
-- +----------+-----------+-----------+-----------+
-- | 张三      |        15 |         0 |         0 |
-- | 李四      |        10 |        15 |         0 |
-- | 王五      |         5 |         0 |         8 |
-- | 赵大      |         0 |         9 |         0 |
-- | 孙二      |         0 |         0 |         6 |
-- +----------+-----------+-----------+-----------+

select username as 用户名,
       sum(case month when '一月份' then ye  else 0  end ) as '一月份',
       sum(case month when '二月份' then ye  else 0  end)  as '二月份',
       sum(case month when '三月份' then ye  else 0  end)  as '三月份'
	from  tb_table1 natural join tb_table2
    group by username;
    
### MYSQL  补充中位数


    
    
### MySQL 6  视图

use hrs;
create table tb_sales
(
sales_id  int unsigned   auto_increment,
sales_date date not null comment '销售日期',
sales_area varchar(20) not null comment '销售区域',
sales_channel varchar(20) not null comment '销售渠道',
order_no varchar(50) not null comment '销售订单',
brand varchar(20) not null comment '品牌',
unite_price integer  not null comment '售价',
sales_quantity integer not null comment '销售数量'
primary key (sales_id)

) engine=innodb comment '销售明细表';

load data local  infile '"C:\Users\Lenovo\Desktop\2020年销售数据.csv"'
into table tb_sales
fields  terminated by  ','
lines terminated by '\n';

-- Error Code: 3948. Loading local data is disabled; this must be enabled on both the client and server sides

--   ##### 表中的数据导入到数据库和数据库的内容导出到表中 ? 
load data local  infile '"C:\Users\Lenovo\Desktop\2020年销售数据.csv"'
into table tb_sales
fields  terminated by  ','
lines terminated by '\n';


-- 1 .查询月度销售金额


select month (sales_date) as 月份,
		  sum(unite_price*sales_quantity) as  销售额
	  from tb_sales
	  group by month(sales_date);
      
      
-- 补充:计算按月环比,

-- 环比=(本期数-上期数)/上期数×100%
-- 同比=(本期数-同期数)÷同期数×100%

-- year /quarter/month/day / weekday

--  窗口函数lag(,数字,默认值0) 可以往回拿几条数据
-- lead( ,数字,默认值) 往后拿几条数据
select 月份,
       (本月销售额-上月销售额)/上月销售额  as 环比
	from (select  月份,
                  销售额 as 本月销售额,
                  lag(销售额,1)  over (order by 月份 asc ) as 上月销售额
			from(select month (sales_date) as 月份,
						sum(unite_price*sales_quantity) as  销售额
					from tb_sales
					group by month(sales_date)) as temp) as temp;
  


-- 2. 查询各品牌对销售额贡献的占比
-- 方法1 定义变量,求出总销售额
select @total_sales := sum(unite_price*sales_quantity) as 销售额
       from tb_sales;
-- 计算各品牌的销售额贡献占比
select brand as  品牌,
       sum(unite_price*sales_quantity)/@total_sales as 销售贡献占比
       from tb_sales
       group by brand;    #这些数据是正确的
       
-- 方法2 直接用一个,不分开写
select brand as  品牌,
       sum(unite_price*sales_quantity)/(select @total_sales := sum(unite_price*sales_quantity) as 销售额
       from tb_sales) as 销售贡献占比
       from tb_sales
       group by brand; 

-- with rollup      -- 算总计

--  方法三: 把需要算的东西提前计算好,加入一些辅助列

--  视图:查询的快照(命名一个一有的查询,下次可以通过这个名字执行对应的查询)
use hrs;
-- 创建视图
create view vw_sales_detail
as  -- 别名
select year(sales_date) as 'year',  -- 年
      quarter(sales_date) as 'quarter',  -- 季度
      month(sales_date) as sales_month,   -- 月
      day(sales_date) as sales_day,    -- 日
      weekday(sales_date) as sales_weekday,  -- 星期几
      sales_channel,    -- 起到
      order_no,   --    
      brand,     -- 品牌
      sales_quantity,    -- 数量
      unite_price*sales_quantity as sales_amount   -- 交易金额
	from tb_sales;
    
-- 查询视图
select * from vw_sales_detail;

-- 也可以这样,创建或替换视图 ,加入一些条件
create  view vw_sales_detail_new
as  -- 别名
select year(sales_date) as 'year',  -- 年
      quarter(sales_date) as 'quarter',  -- 季度
      month(sales_date) as sales_month,   -- 月
      day(sales_date) as sales_day,    -- 日
      weekday(sales_date) as sales_weekday,  -- 星期几
      sales_channel,    -- 起到
      order_no,   --    
      brand,     -- 品牌
      case when unite_price <300 then 'low' when unite_price <800 then 'middle' else 'high'  end  as  price_segment,  -- 对价格进行等级划分
      sales_quantity,    -- 数量
      unite_price*sales_quantity as sales_amount   -- 交易金额
	from tb_sales;    

-- 1 .查询月度销售金额
select  sales_month,
        sales_amount
	from vw_sales_detail_new
    group by sales_month;
    
-- 借助视图来计算销售额月环比
-- 环比=(本期数-上期数)/上期数×100%
select  月份,
		(月销售额-上月销售额)/上月销售额  as 月环比
	from (select 月份,
		         月销售额,   
                 lag(月销售额,1) over (order  by 月份 asc )as  上月销售额
			 from (select  sales_month as 月份,
						   sum(sales_amount) as 月销售额
					 from vw_sales_detail_new
					 group by sales_month) as temp  ) as temp;
 




	
-- 2. 查询各品牌对销售额贡献的占比
select brand,
       sales_amount/sum(sales_amount) as 销售额贡献
	from vw_sales_detail_new
    group by brand;



-- 3. 查询各销售区域月度销售额  --- 宽表
-- 方法1 没有使用视图,再原表中进行操作
select sales_area as 渠道,
       month(sales_date)  as 月份,
	   sum(case  month(sales_date) when  1 then unite_price*sales_quantity  else 0 end ) as '1月销售额',
	   sum(case  month(sales_date)  when  2 then unite_price*sales_quantity  else 0 end ) as '2月销售额',
       sum(case  month(sales_date) when  3 then unite_price*sales_quantity  else 0 end ) as '3月销售额',
	   sum(case  month(sales_date) when  4 then unite_price*sales_quantity  else 0 end ) as '4月销售额',
       sum(case  month(sales_date) when  5 then unite_price*sales_quantity  else 0 end ) as '5月销售额',
	   sum(case  month(sales_date) when  6 then unite_price*sales_quantity  else 0 end ) as '6月销售额',
       sum(case  month(sales_date) when  7 then unite_price*sales_quantity  else 0 end ) as '7月销售额',
	   sum(case  month(sales_date) when  8 then unite_price*sales_quantity else 0 end ) as '8月销售额',
       sum(case  month(sales_date) when  9 then unite_price*sales_quantity  else 0 end ) as '9月销售额',
	   sum(case  month(sales_date) when  10 then unite_price*sales_quantity  else 0 end ) as '10月销售额',
       sum(case  month(sales_date) when  11 then unite_price*sales_quantity  else 0 end ) as '11月销售额',
	   sum(case  month(sales_date) when  12 then unite_price*sales_quantity  else 0 end ) as '12月销售额'
	from tb_sales
    group by sales_area;
    
-- 方法2 使用视图,先根据需要的数据,键视图表
-- 3. 查询各销售区域月度销售额  --- 宽表
create view  vw_sales_detail_add
as 
 select sales_area,
		month(sales_date) as sales_month,
        unite_price * sales_quantity as sales_amount
	from tb_sales;
--  3. 查询各销售区域月度销售额  --- 宽表
select sales_area,
	   sum(case  sales_month when  1 then  sales_amount else 0 end ) as '1月销售额',
	   sum(case  sales_month  when  2 then sales_amount else 0 end ) as '2月销售额',
       sum(case  sales_month when  3 then sales_amount else 0 end ) as '3月销售额',
	   sum(case  sales_month when  4 then sales_amount  else 0 end ) as '4月销售额',
       sum(case  sales_month when  5 then sales_amount  else 0 end ) as '5月销售额',
	   sum(case  sales_month when  6 then sales_amount  else 0 end ) as '6月销售额',
       sum(case  sales_month when  7 then sales_amount  else 0 end ) as '7月销售额',
	   sum(case  sales_month when  8 then sales_amount else 0 end ) as '8月销售额',
       sum(case  sales_month when  9 then sales_amount else 0 end ) as '9月销售额',
	   sum(case  sales_month when  10 then sales_amount else 0 end ) as '10月销售额',
       sum(case  sales_month when  11 then sales_amount  else 0 end ) as '11月销售额',
	   sum(case  sales_month when  12 then sales_amount  else 0 end ) as '12月销售额'
	from vw_sales_detail_add
    group by sales_area;

  
  
-- 4. 查询各渠道品牌销量  -- 宽表

select sales_channel as 销售渠道,
	   sum(case  brand  when  '八匹马' then sales_quantity else 0 end  )  as '八匹马',
	   sum(case  brand when   '皮皮虾' then sales_quantity else 0 end  )as '皮皮虾',
	   sum(case  brand when  '壁虎' then sales_quantity else 0 end  ) as '壁虎',
	   sum(case  brand when  '花花姑娘' then sales_quantity else 0 end)  as '花花姑娘',
	   sum(case  brand when  '啊哟喂' then sales_quantity  else 0 end ) as '啊哟喂'
	   
	from vw_sales_detail_new
    group by 销售渠道;
    

-- 5. 不同价格区间产品的月度销量
select price_segment  as 价格区间,
       sales_month  as 月份,
       sum(sales_quantity) as 销量
from vw_sales_detail_new
group by sales_month ;

-- 按照不同的价格区间做月度销量分析
-- 写成宽表
-- 根据价格段进行分组
use hrs;
select sales_month as 月份,
	   sum(case  price_segment when  'low' then sales_quantity  else  'o' end) as '低价销售量', 
	   sum(case  price_segment when  'middle' then sales_quantity  else  'o' end) as '中价销售量', 
	   sum(case  price_segment when  'high' then sales_quantity  else  'o' end) as '高价销售量'
  from vw_sales_detail_new
  group by sales_month;
use hrs;

### MySQL 7  函数

use school;
-- 函数 :功能上相对独立且会被重复使用的代码     --- 用户自定义函数
-- 如果学院介绍超过了50个字符,截断它后补上省略号
-- 如果学院介绍没有超过50个字符,就完整到的显示出来
select col_id,
       col_name,
       case when char_length(col_intro) > 50 then   concat( left(col_intro,50) , '......' ) else  col_intro  end as col_intro
 from tb_college;
 
 -- 修改代码终止符
 --函数只能的分号表示一个语句的结束,而不是整个代码的结束
 delimiter $$
    
--  创建函数  User Defined Function
-- reads sql data 如果使用了sql语句,在最后面要加一个说明,没有使用sql语句,就写no sql

-- 自定义一个不需要传参的函数
 delimiter $$
create function say_hello()  returns varchar(16383) reads sql data no sql
begin 
    return 'hello,world';
end $$
delimiter ; 

use school;
-- 练习  from dual 是一个虚拟表,只是让代码完整。
select say_hello() from dual;

-- 自定义一个需要传参的函数
delimiter $$
create function say_name( tb_name varchar(30))  returns varchar(16383) reads sql data no sql
begin 
    return  concat('hello, ',tb_name , '!');
end $$
delimiter ; 
select say_name('tom') from dual;
select say_name('kaixing') from dual;


-- 把字符串截断变成函数
delimiter $$
create function truncate_string(
    content varchar(16383),
    max_length int unsigned
)  returns varchar (16383) no sql 
begin 
    if char_length(content) > max_length  then 
          return   concat(left(content,max_length),'......');
	else 
          return content;
	end   if;
     

end $$
delimiter ;


-- 可以定义变量,让函数变得更简洁

delimiter $$
create function truncate_string(
    content varchar(16383),
    max_length int unsigned
)  returns varchar (16383) no sql 
begin 
    declare result varchar(16383) default content;
    if char_length(content) > max_length  then 
         set result=concat(left(content,max_length),'......');
	end  if;
	return result;
     
end $$
delimiter ;

use school;

-- 使用自定义函数来解决前面的问题
select col_id,
       col_name,
       truncate_string(col_intro,50)
 from tb_college ;
 
##### practice 
 -- 杨过 --> 杨*
 -- 杨不悔-- > 杨*悔
 -- 东方不败--> 东**败
 ##### Practice 2 YY
 -- 1:函数和过程有什么用?二者有什么区别?
 -- 2:base64是什么?
 -- 1. BASE64编码的原理和应用场景
--  2. 自定义函数实现保护姓名隐私
--  3. 函数和过程有什么作用?二者有何区别?
 -- 4. 绘制school数据库对应的ER图
 
 drop function if exists protect_name;
 use school;
  delimiter $$
 create function protect_name(thy_name varchar(16383))
 returns varchar(16383) no sql
 begin
      declare result varchar(16383) default thy_name;
      if  char_length(thy_name) = 2 then 
             set result = concat(left(thy_name,1),'*');
      else  
            set result = concat(left(thy_name,1),repeat('*',(char_length(thy_name)-2)),right(thy_name,1));
	  end  if;
	  return result;
 end$$
 delimiter ;
 
 select protect_name(stu_name)
     from tb_student;
 
### MySQL 8 JSON数据查询
use hrs;
-- json 对象的数据查询
drop table if exists `tb_test`;

create table `tb_test`
(
user_id  bigint unsigned,
login_info json,
primary key (`user_id`)
)engine=innodb;

insert into `tb_test` values 
    (1, '{"tel": "13122335566", "QQ": "654321", "wechat": "jackfrued"}'),
    (2, '{"tel": "13599876543", "weibo": "wangdachui123"}'),
    (3, '{"wechat": "luohao"}');

select 
      user_id,
      json_unquote(json_extract(login_info, '$.tel')) as 手机号
 from tb_test; 
    

select 
    `user_id`,
    json_unquote(json_extract(`login_info`, '$.tel')) as 手机号,
    json_unquote(json_extract(`login_info`, '$.wechat')) as 微信
from `tb_test`;


select 
    `user_id`,
    `login_info` ->> '$.tel' as 手机号,
    `login_info` ->> '$.wechat' as 微信
from `tb_test`;



--*****  用户打标签

create table `tb_tags`
(
`tag_id` int unsigned not null comment '标签ID',
`tag_name` varchar(20) not null comment '标签名',
primary key (`tag_id`)
) engine=innodb;

insert into `tb_tags` (`tag_id`, `tag_name`) 
values
    (1, '70后'),
    (2, '80后'),
    (3, '90后'),
    (4, '00后'),
    (5, '爱运动'),
    (6, '高学历'),
    (7, '小资'),
    (8, '有房'),
    (9, '有车'),
    (10, '爱看电影'),
    (11, '爱网购'),
    (12, '常点外卖');
    
drop table  if exists `tb_users_tags`;
create table `tb_users_tags`
(
`user_id` bigint unsigned not null comment '用户ID',
`user_tags` json not null comment '用户标签'
) engine=innodb;

insert into `tb_users_tags` values 
    (1, '[2, 6, 8, 10]'),
    (2, '[3, 10, 12]'),
    (3, '[3, 8, 9, 11]');
 delete from tb_users_tags where user_id=1;
    
-- 查询爱看电影(有10这个标签)的用户ID    
select user_id
   from tb_users_tags
   where 10 member of (user_tags -> '$'); -- $代表的是user_tags的数组
   
-- 查询爱看(10)电影的80后(2)
-- json_contains( 列名-> '$' ,'[ 2,10]')  前面的数组有么有包含后面的数组(超集)

select user_id
   from tb_users_tags
   where json_contains (user_tags -> '$','[2,10]'); 


-- 查询爱看电影或80或90后的用户ID
-- json_overlaps(user_tags ->'$','[2,3,10]' ) $与[2,3,10] 这两个是不是有交集,有交集就留下来

select user_id
  from tb_users_tags
  where json_overlaps(user_tags -> '$','[2,3,10]');
  
  
### MySQL 9 过程与索引


 -- 科普知识
 -- * 把明文变成密文的过程叫加密,把密文变回明文的过程叫做解密
   --  对称加密:加密和解密是同一个密密匙
   -- 非对称加密:加密和集美不是一个密钥,有公钥和私钥之分。
   --  摘要: 通过一个单向哈希函数给一个对象生成一个签名(指纹)。
       -- MD5/SHA1/SHA256
	-- 编码:把一种表示方式转换成另外的表示方式,逆操作叫做解码。
 

 
  -- 单机 —> 多级    复杂均衡   分布式集群   数据如何同步??
-- 事务transaction - 把多个写数据的操作(增删改)视为不可分割的原子性操作,要么全都成功,要么全都失败。 程序 :procedure
 
 -- 过程学习
 -- 互联网会因为遭遇高并发和大流量,所以不推荐使用存储过程,使用存储过程会增加
 use hrs;
 
 -- 工资普调
 -- 规则 : 10部门 +500, 20部门+1000 ,30部门+300 
 update tb_emp set sal = sal+50 where dno = 10;
 update tb_emp set sal = sal+1000 where dno = 20;
 update tb_emp set sal = sal+300 where dno = 30;
 
 -- TCL----transction  Control Language
 -- start transaction /begin /commit /roolback
 -- 如何判断代码执行是否成功,方法,如果代码出现异常,代码会打断,所有加一个异常处理器,
 -- 存储过程的参数,两个,一个输入参数in(把信息代入过程中)和输出参数out(把数据带出过程),变量名前面加@表示这个变量是用户自定义的一个变量
 -- 想要把过程得到的数据,拿出来用,就要加入输出参数,看例2
 -- 例1
 -- 使用过程来完成调薪:
 delimiter $$
 create procedure sp_upgrade_salary( )   -- 如果需要参数,可以传参。
 begin 
      declare flag boolean default 1;
      declare continue handler for sqlexception set flag = 0;
      start transaction;
      -- 下面这个三条命令是原子性操作,必须全部成功。要么全部失败
	  update tb_emp set sal = sal+500 where dno = 10;
      update tb_emp set sal = sal+1000 where dno = 20;
      update tb_emp set sal = sal+300 where dno = 30;
      if falg then
          commit;
      else
         rollback ;
         end if ;
 
 end $$
delimiter ;


 -- 调用过程的程序
call sp_upgrade_salary();

-- mysql必知必会上第24章会讲到使用游标cursor;来抓取二维表数据

drop  procedure sp_upgrade_salary # 删除存储过程


 -- 例2 
 delimiter $$
 create procedure sp_upgrade_salary( out avg_sal decimal(10,2))   -- 如果需要参数,可以传参out 是输出参数,通过输出参数,将过程中的数据带出去使用。
 begin 
      declare flag boolean default 1;
      declare continue handler for sqlexception set flag = 0;
      start transaction;
      -- 下面这个三条命令是原子性操作,必须全部成功。要么全部失败
	  update tb_emp set sal = sal+500 where dno = 10;
      update tb_emp set sal = sal+1000 where dno = 20;
      update tb_emp set sal = sal+300 where dno = 30;
      if falg then
          commit;
      else
         rollback ;
         end if ;
         select avg(sal) into avg_sal from tb_emp;
 end $$
delimiter ;

-- 调用过程存储程序
call sp_upgrade_salary(@x);
select @x;
select @x from tb_emp where sal > @x ;


推荐阅读