1.五星:count 函数实现 dense_rank() over()
# 五星:重要 : count() 函数实现 dense_rank() over()
# 74
# 每个性别分数排名前2的用户,得到的结果先按照的 先按照salary降序排序 ,然后name升序排序
select * from employees_59 ;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |垂 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|峰 |萧 |男 |2021-01-23|
# |5 |2021-01-23|远山 |萧 |男 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from salaries_60;
# +------+------+----------+----------+---+
# |emp_no|salary|from_date |to_date |sex|
# +------+------+----------+----------+---+
# |1 |100 |2021-01-23|9999-01-01|0 |
# |2 |1000 |2021-01-23|9999-01-01|1 |
# |3 |10000 |2021-01-23|9999-01-01|1 |
# |4 |500 |2021-01-23|9999-01-01|0 |
# |5 |5000 |2021-01-23|9999-01-01|1 |
# +------+------+----------+----------+---+
# 每个性别分数排名前2的用户,得到的结果先按照的 先按照salary降序排序 ,然后name升序排序
# 思路一: 用分析函数,分组后内部排序然后取前两条数据
select
s.salary,
e.last_name,
e.first_name,
dense_rank() over (partition by s.sex order by s.salary desc , e.last_name asc) as s_rn
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no;
# +------+---------+----------+----+
# |salary|last_name|first_name|s_rn|
# +------+---------+----------+----+
# |500 |萧 |峰 |1 |
# |100 |一光 |郭 |2 |
# |10000 |雪 |慕容 |1 |
# |5000 |萧 |远山 |2 |
# |1000 |垂 |慕容 |3 |
# +------+---------+----------+----+
# 上述sql 加限制条件 s_rn <=2 ,取得组内的排名前 二名 的数据
select
s.salary,
e.last_name,
e.first_name,
dense_rank() over (partition by s.sex order by s.salary desc , e.last_name asc) as s_rn
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no and s_rn <=2;
# 识别不了别名,戴个套
select * from (
select
s.salary,
e.last_name,
e.first_name,
dense_rank() over (partition by s.sex order by s.salary desc , e.last_name asc) as s_rn
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no
) A where A.s_rn <=2;
# ------+---------+----------+----+
# |salary|last_name|first_name|s_rn|
# +------+---------+----------+----+
# |500 |萧 |峰 |1 |
# |100 |一光 |郭 |2 |
# |10000 |雪 |慕容 |1 |
# |5000 |萧 |远山 |2 |
# +------+---------+----------+----+
# 每个性别分数排名前2的用户,得到的结果先按照salary降序排序 ,然后name升序排序
# 思路二: count() 实现 dense_rank() over()
select s.salary,
e.last_name,
e.first_name,
s.sex
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no group by s.sex ;
# +------+---------+----------+---+
# |salary|last_name|first_name|sex|
# +------+---------+----------+---+
# |100 |一光 |郭 |0 |
# |1000 |垂 |慕容 |1 |
# +------+---------+----------+---+
select s.salary,
e.last_name,
s.sex
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no group by s.sex ,s.salary, e.last_name;
# +------+---------+---+
# |salary|last_name|sex|
# +------+---------+---+
# |100 |一光 |0 |
# |1000 |垂 |1 |
# |10000 |雪 |1 |
# |500 |萧 |0 |
# |5000 |萧 |1 |
# +------+---------+---+
# 在此基础上 增加一列:排名(同一个组内部的排名,所以需要加上 s.sex = s2.sex 这个限制条件)
select s.salary,
e.last_name,
s.sex,
(select count(distinct salary) from salaries_60 s2 where s.salary>= s2.salary and s.sex = s2.sex) as rn
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no group by s.sex ,s.salary, e.last_name ;
# 在此结果上 and rn <=2 过滤数据
select * from (
select s.salary,
e.last_name,
s.sex,
(select count(distinct salary) from salaries_60 s2 where s.salary>= s2.salary and s.sex = s2.sex) as rn
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no group by s.sex ,s.salary, e.last_name
)A where A.rn <=2 ;
# +------+---------+---+--+
# |salary|last_name|sex|rn|
# +------+---------+---+--+
# |100 |一光 |0 |1 |
# |1000 |垂 |1 |1 |
# |500 |萧 |0 |2 |
# |5000 |萧 |1 |2 |
# +------+---------+---+--+
# count 的另一种写法:这个不需要戴套
# 先分组,不考虑组内前两名问题
select s.salary,
e.last_name,
s.sex
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no
group by s.sex ,s.salary, e.last_name ;
# +------+---------+---+
# |salary|last_name|sex|
# +------+---------+---+
# |100 |一光 |0 |
# |1000 |垂 |1 |
# |10000 |雪 |1 |
# |500 |萧 |0 |
# |5000 |萧 |1 |
# +------+---------+---+
#
# 加上 where条件 ,限制同一组内的 前两名数据
select s.salary,
e.last_name,
s.sex
from employees_59 e
inner join salaries_60 s on e.emp_no = s.emp_no
where (select count(distinct salary) from salaries_60 s2 where s.salary>= s2.salary and s.sex = s2.sex) <=2
group by s.sex ,s.salary, e.last_name ;
1. 五星:(排序问题)
# 重要:五星
# 54 (难度范围:一般难度 但是思路很好:考察点:排序 dense_rank() over() ,和 count的转换,以及 count 的位置:select 后面 ; where count() <3 取前三名数据)
# 查找排除最大、最小salary之后的当前(to_date = '9999-01-01' )员工的平均工资avg_salary。
# CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,
# `salary` int(11) NOT NULL,
# `from_date` date NOT NULL,
# `to_date` date NOT NULL,
# PRIMARY KEY (`emp_no`,`from_date`));
# 最大最下问题()排序问题 三个思路: ① 排除掉最大最下 ②count ③分析函数
select * from salaries_60;
# +------+------+----------+----------+---+
# |emp_no|salary|from_date |to_date |sex|
# +------+------+----------+----------+---+
# |1 |100 |2021-01-23|9999-01-01|0 |
# |2 |1000 |2021-01-23|9999-01-01|1 |
# |3 |10000 |2021-01-23|9999-01-01|1 |
# |4 |500 |2021-01-23|9999-01-01|0 |
# |5 |5000 |2021-01-23|9999-01-01|1 |
# +------+------+----------+----------+---+
# 思路一: 排除掉最大最下值
select avg(salary)
from salaries_60
where salary not in (select max(salary) from salaries_60 union all select min(salary) from salaries_60);
# +-----------+
# |avg(salary)|
# +-----------+
# |2166.6667 |
# +-----------+
# 确认下
select avg(salary)
from (
select salary
from salaries_60
where salary not in (select max(salary) from salaries_60 union all select min(salary) from salaries_60)
) A;
# +-----------+
# |avg(salary)|
# +-----------+
# |2166.6667 |
# +-----------+
# or (范围查询)
select avg(salary) as avg_salary
from salaries_60
where salary <
(
select max(salary)
from salaries_60
where to_date = '9999-01-01'
)
and salary >
(
select min(salary)
from salaries_60
where to_date = '9999-01-01'
)
and to_date = '9999-01-01' ;
# 思路二:分析函数 rownum() over
select s.salary ,
row_number() over (order by s.salary desc ) as rm
from salaries_60 s ;
# +------+--+
# |salary|rm|
# +------+--+
# |10000 |1 |
# |5000 |2 |
# |1000 |3 |
# |500 |4 |
# |100 |5 |
# +------+--+
# 然后排除掉最大和最小的行数 ??
select salary
from (
select s.salary,
dense_rank() over (order by s.salary desc ) as rm
from salaries_60 s
) A
# 第一 肯定 rm 值为 1
where rm > 1
and rm < (
# 子查询获得最大排名(rm 是个定值,定值的话考虑子查询来实现)
select max(rm)
from (
select dense_rank() over (order by s.salary desc ) as rm
from salaries_60 s
) B
);
# +------+
# |salary|
# +------+
# |5000 |
# |1000 |
# |500 |
# +------+
# 在此基础上取平均值
select avg(salary)
from (
select s.salary,
dense_rank() over (order by s.salary desc ) as rm
from salaries_60 s
) A
# 第一 肯定 rm 值为 1
where rm > 1
and rm < (
# 子查询获得最大排名(rm 是个定值,定值的话考虑子查询来实现)
select max(rm)
from (
select dense_rank() over (order by s.salary desc ) as rm
from salaries_60 s
) B
);
# +-----------+
# |avg(salary)|
# +-----------+
# |2166.6667 |
# +-----------+
#错误sql: 识别不了 mm , 戴个套 (这个sql 有问题,结果不对,max 的位置不对)
select avg(salary) from (
select salary ,rm ,max(rm) from (
select s.salary ,
dense_rank() over (order by s.salary desc ) as rm
from salaries_60 s group by salary
) A where rm > 1
) B where B.rm < max(rm) ;
# +-----------+
# |avg(salary)|
# +-----------+
# |5000.0000 |
# +-----------+
# 思路三: ②count
select s1.salary,
(select count(distinct salary) from salaries_60 s2 where s1.salary <=s2.salary) as rm
from salaries_60 s1 where rm in (2,rm-1);
# 错误sql:识别不了别名 戴套
select * from (
select s1.salary,
(select count(distinct salary) from salaries_60 s2 where s1.salary <=s2.salary) as rm
# from salaries_60 s1 order by salary desc
# 或者下面语句(注释掉的和下面的是 等效的)
from salaries_60 s1 order by rm
) A where rm in (2,rm-1);
# +------+--+
# |salary|rm|
# +------+--+
# |5000 |2 |
# +------+--+
#分析:错误原因:
# +------+--+
# |salary|rm|
# +------+--+
# |10000 |1 |
# |5000 |2 |
# |1000 |3 |
# |500 |4 |
# |100 |5 |
# +------+--+
# rm = 1 ,where rm in (2,rm-1) ---> where rm in (2,0) 不取
# rm = 2 ,where rm in (2,rm-1) ---> where rm in (2,1) 可以取到 rm = 2 这条数据
# rm = 3 ,where rm in (2,rm-1) ---> where rm in (2,2) 不取
# rm = 4 ,where rm in (2,rm-1) ---> where rm in (2,3) 不取
# rm = 5 ,where rm in (2,rm-1) ---> where rm in (2,4) 不取
# 综上 in 的话,就只能两个值,目的要的是多个 的范围值(between and ,大于等于小于 etc)
# 正确sql:
select *
from (
select s1.salary,
(select count(distinct salary) from salaries_60 s2 where s1.salary <= s2.salary) as rm
# from salaries_60 s1 order by salary desc
# 或者下面语句(注释掉的和下面的是 等效的)
from salaries_60 s1
order by rm
) A
where rm > 1
and rm < (
# 获取排名序号最大的
select max((select count(distinct salary) cnt from salaries_60 s2 where s3.salary <= s2.salary)) as maxRn
from salaries_60 s3
);
# +------+--+
# |salary|rm|
# +------+--+
# |5000 |2 |
# |1000 |3 |
# |500 |4 |
# +------+--+
# 在此基础上进行求平均值
select avg(salary)
from (
select s1.salary,
(select count(distinct salary) from salaries_60 s2 where s1.salary <= s2.salary) as rm
# from salaries_60 s1 order by salary desc
# 或者下面语句(注释掉的和下面的是 等效的)
from salaries_60 s1
order by rm
) A
where rm > 1
and rm < (
# 获取排名序号最大的(具体思路看下面的 一 )
select max((select count(distinct salary) cnt from salaries_60 s2 where s3.salary <= s2.salary)) as maxRn
from salaries_60 s3
);
# +-----------+
# |avg(salary)|
# +-----------+
# |2166.6667 |
# +-----------+
# (一)获取最大的序列号(排名号)
select A.salary,cnt from (
select s3.salary,
(select count(distinct salary) cnt from salaries_60 s2 where s3.salary <=s2.salary) as cnt
from salaries_60 s3 order by cnt
) A ;
# +------+---+
# |salary|cnt|
# +------+---+
# |10000 |1 |
# |5000 |2 |
# |1000 |3 |
# |500 |4 |
# |100 |5 |
# +------+---+
# 单纯的获取排名序号
select (select count(distinct salary) cnt from salaries_60 s2 where s3.salary <= s2.salary) as cnt
from salaries_60 s3
order by cnt;
# +---+
# |cnt|
# +---+
# |1 |
# |2 |
# |3 |
# |4 |
# |5 |
# +---+
# 简化sql
select max(cnt) from (
select
(select count(distinct salary) cnt from salaries_60 s2 where s3.salary <=s2.salary) as cnt
from salaries_60 s3 order by cnt
) A ;
# +--------+
# |max(cnt)|
# +--------+
# |5 |
# +--------+
# 再次简化(去掉套儿)
select
max((select count(distinct salary) cnt from salaries_60 s2 where s3.salary <=s2.salary) ) as maxRn
from salaries_60 s3 ;
# +-----+
# |maxRn|
# +-----+
# |5 |
# +-----+
# or count 的另一种写法(count 放到 where 后面)
select avg(salary)
from (
select s1.salary
from salaries_60 s1
) A
# 说明: (select count(distinct salary) from salaries_60 s2 where A.salary <= s2.salary) 这个sql是某行数据的排名号
where (select count(distinct salary) from salaries_60 s2 where A.salary <= s2.salary) > 1
and (select count(distinct salary) from salaries_60 s2 where A.salary <= s2.salary) < (
# 获取排名序号最大的(具体思路看下面的 一 )
select max((select count(distinct salary) cnt from salaries_60 s2 where s3.salary <= s2.salary)) as maxRn
from salaries_60 s3
);
# +-----------+
# |avg(salary)|
# +-----------+
# |2166.6667 |
# +-----------+
# 可以简化(count 放到后面是直接作为条件的,不需要戴套儿)---> 最终sql
select avg(s1.salary)
from salaries_60 s1
# 说明: (select count(distinct salary) from salaries_60 s2 where A.salary <= s2.salary) 这个sql是某行数据的排名号
where (select count(distinct salary) from salaries_60 s2 where s1.salary <= s2.salary) > 1
and (select count(distinct salary) from salaries_60 s2 where s1.salary <= s2.salary) < (
# 获取排名序号最大的(具体思路看下面的 一 )
select max((select count(distinct salary) cnt from salaries_60 s2 where s3.salary <= s2.salary)) as maxRn
from salaries_60 s3
);
# +--------------+
# |avg(s1.salary)|
# +--------------+
# |2166.6667 |
# +--------------+
# 思路四:聚合函数(技巧)
SELECT (SUM(salary) - MAX(salary) - MIN(salary)) / (COUNT(1)-2) as avg_salary
FROM salaries_60 where to_date = '9999-01-01';
# +----------+
# |avg_salary|
# +----------+
# |2166.6667 |
# +----------+
1.五星:(重要:思路很好)
count 函数实现 dense_rank() over()
# 五星:重要 count() 函数 如何实现 dense_rank() over() 的功能
# 61 对于employees表中,输出first_name排名(按first_name升序排序)为奇数的first_name
CREATE TABLE `employees_61` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
# 如,输入为:
INSERT INTO employees_61 VALUES(10001,'1953-09-02','Georgi','Facello','M','1986-06-26');
INSERT INTO employees_61 VALUES(10002,'1964-06-02','Bezalel','Simmel','F','1985-11-21');
INSERT INTO employees_61 VALUES(10005,'1955-01-21','Kyoichi','Maliniak','M','1989-09-12');
INSERT INTO employees_61 VALUES(10006,'1953-04-20','Anneke','Preusig','F','1989-06-02');
select * from employees_61;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |10001 |1953-09-02|Georgi |Facello |M |1986-06-26|
# |10002 |1964-06-02|Bezalel |Simmel |F |1985-11-21|
# |10005 |1955-01-21|Kyoichi |Maliniak |M |1989-09-12|
# |10006 |1953-04-20|Anneke |Preusig |F |1989-06-02|
# +------+----------+----------+---------+------+----------+
# 对于employees表中,输出first_name排名(按first_name升序排序)为奇数的first_name
# 取模函数:mod(m,n),n为被除数
# 思路:
select first_name ,dense_rank() over (order by first_name) as dr from employees_61 where mod(dr,2)!=0;
#因为识别不了别名,所以戴个套
select first_name from (
select first_name ,dense_rank() over (order by first_name) as dr from employees_61
) A where mod(dr,2)!=0;
# +----------+
# |first_name|
# +----------+
# |Anneke |
# |Georgi |
# +----------+
# 不用 mod 函数
select first_name from (
select first_name ,dense_rank() over (order by first_name) as dr from employees_61
) A where dr%2 = 1;
# +----------+
# |first_name|
# +----------+
# |Anneke |
# |Georgi |
# +----------+
# 思路三 :涉及到排名 count() 函数 (此处不考虑 dense_rank() over )
select first_name ,
# 一定要去重,否则排名会有问题
(select count(distinct first_name) from employees_61 e2 where e.first_name <= e2.first_name ) as rn
from employees_61 e where rn%2 = 1 ;
# 识别不了别名,戴个套
select first_name ,rn
from (
select first_name,
# 一定要去重,否则排名会有问题
(select count(distinct first_name) from employees_61 e2 where e.first_name >= e2.first_name) as rn
from employees_61 e
) A
where A.rn % 2 = 1;
# +----------+--+
# |first_name|rn|
# +----------+--+
# |Georgi |3 |
# |Anneke |1 |
# +----------+--+
1.五星(重要:分析函数,新知识点)
# 由一个sql引入的分析函数 sum() over()
# 60
# 按照salary的累计和running_total,其中running_total为前N个当前( to_date = '9999-01-01')员工的salary累计和,其他以此类推。
# 具体结果如下Demo展示。
CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
select * from salaries_60;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# |4 |500 |2021-01-23|9999-01-01|
# |5 |5000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
# running_total
select s.emp_no,
s.salary,
row_number() over (order by emp_no asc) as rn,
(select sum(s2.salary), row_number() over (order by emp_no asc) as rn2 from salaries_60 s2 where s2.rn2 <= s.rn) as running_total
from salaries_60 s;
# 识别不了别名 ,进行改写 (下面是个错误的sql)
select A.emp_no, A.salary, A.rn ,
(
select B.sm from (
select sum(s2.salary) as sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
) B where B.rn2 <= A.rn
) as running_total
from (
select s.emp_no,
s.salary,
row_number() over (order by emp_no asc) as rn
from salaries_60 s
) A ;
# +------+------+--+-------------+
# |emp_no|salary|rn|running_total|
# +------+------+--+-------------+
# |1 |100 |1 |16600 |
# |2 |1000 |2 |16600 |
# |3 |10000 |3 |16600 |
# |4 |500 |4 |16600 |
# |5 |5000 |5 |16600 |
# +------+------+--+-------------+
#避免 is incompatible with sql_mode=only_full_group_by 这个错误
set session sql_mode='';
select B.sm from (
select sum(s2.salary) as sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
) B where B.rn2 <= 3 ;
select sum(s2.salary) from salaries_60 s2;
# +--------------+
# |sum(s2.salary)|
# +--------------+
# |16600 |
# +--------------+
# 下面sql错误:sum(salary) 会对 salaries_60 表的所有数据求和,导致 B的 sm 是一个常量
select sm
from (
select sum(salary) sm, row_number() over (order by emp_no asc) rn2 from salaries_60 s2
) B
where B.rn2 <= 2;
# +-----+
# |sm |
# +-----+
# |16600|
# +-----+
# 正确解法
select sum(sm)
from (
select salary sm, row_number() over (order by emp_no asc) rn2 from salaries_60 s2
) B
# 识别不了 s2 ,所以加个套 ,where B.rn2 <= 2
where B.rn2 <= 2;
# +-------+
# |sum(sm)|
# +-------+
# |1100 |
# +-------+
# 正确sql
select A.emp_no, A.salary, A.rn ,
(
select sum(sm) from (
select s2.salary as sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
) B where B.rn2 <= A.rn
) as running_total
from (
select s.emp_no,
s.salary,
row_number() over (order by emp_no asc) as rn
from salaries_60 s
) A ;
# +------+------+--+-------------+
# |emp_no|salary|rn|running_total|
# +------+------+--+-------------+
# |1 |100 |1 |100 |
# |2 |1000 |2 |1100 |
# |3 |10000 |3 |11100 |
# |4 |500 |4 |11600 |
# |5 |5000 |5 |16600 |
# +------+------+--+-------------+
# 法二:(用分析函数)
SELECT emp_no, salary, SUM(salary) OVER (ORDER BY emp_no) AS running_total
FROM salaries
WHERE to_date = '9999-01-01';
# mysql的分析函数
# 分析函数之连续求和sum(…) over(…)
# 分析函数之排序值rank()和dense_rank()
# 分析函数之排序后顺序号row_number()
# 分析函数之取上下行数据lag()和lead()
# 分析函数和聚合函数的区别
# 普通的聚合函数用group by分组,每个分组返回一个统计值,
# 分析函数采用partition by分组,并且每组每行都可以返回一个统计值。
# 语法
# FUNCTION_NAME(<参数>,…)
# OVER (<PARTITION BY 表达式,…>
# <ORDER BY 表达式 <ASC DESC>
# <NULLS FIRST NULLS LAST>>
# <WINDOWING子句>)
# 分析函数带有一个开窗函数over(),包含三个分析子句:
#
# 分组(partition by)
# 排序(order by)
# 窗口(rows)
# 规则:
# sum(...) over( ),对所有行求和
#
# sum(...) over( order by ... ), 连续求和
#
#
# sum(...) over( partition by... ),同组内所行求和
#
# sum(...) over( partition by... order by ... ),同第1点中的排序求和原理,只是范围限制在组内
# 总结
# 在"… from emp;"后面不要加order by 子句,使用的分析函数的(partition by deptno order by sal)
# 里已经有排序的语句了,如果再在句尾添加排序子句,一致倒罢了,不一致,结果就令人费劲了
# ① 连续求和
# 连续求和分析函数 sum(…) over(…)
# 【功能】连续求和分析函数
# 【参数】具体参示例
SELECT *
FROM salaries_60 s ;
# +------+------+----------+----------+---+
# |emp_no|salary|from_date |to_date |sex|
# +------+------+----------+----------+---+
# |1 |100 |2021-01-23|9999-01-01|0 |
# |2 |1000 |2021-01-23|9999-01-01|1 |
# |3 |10000 |2021-01-23|9999-01-01|1 |
# |4 |500 |2021-01-23|9999-01-01|0 |
# |5 |5000 |2021-01-23|9999-01-01|1 |
# +------+------+----------+----------+---+
# 对所有行求和
SELECT s.emp_no, s.salary , sum(s.salary) over() as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so |
# +------+------+-----+
# |1 |100 |16600|
# |2 |1000 |16600|
# |3 |10000 |16600|
# |4 |500 |16600|
# |5 |5000 |16600|
# +------+------+-----+
# 连续求和(连续的话就得加order by)
SELECT s.emp_no, s.salary , sum(s.salary) over(order by emp_no) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so |
# +------+------+-----+
# |1 |100 |100 |
# |2 |1000 |1100 |
# |3 |10000 |11100|
# |4 |500 |11600|
# |5 |5000 |16600|
# +------+------+-----+
# group by 后,每个组 返回一个结果
SELECT sum(s.salary)
FROM salaries_60 s group by s.emp_no, s.salary;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |100 |
# |1000 |
# |10000 |
# |500 |
# |5000 |
# +-------------+
# group by 后,每个组 返回一个结果
set session sql_mode='';
SELECT sum(s.salary)
FROM salaries_60 s group by s.salary;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |100 |
# |1000 |
# |10000 |
# |500 |
# |5000 |
# +-------------+
SELECT sum(s.salary)
FROM salaries_60 s ;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |16600 |
# +-------------+
# sum(sal) over (order by deptno,ename) 按emp_no 和 sex “连续”求总和
SELECT s.emp_no, s.salary , sum(s.salary) over(order by emp_no ,sex) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so |
# +------+------+-----+
# |1 |100 |100 |
# |2 |1000 |1100 |
# |3 |10000 |11100|
# |4 |500 |11600|
# |5 |5000 |16600|
# +------+------+-----+
# 同组内求和
SELECT s.emp_no, s.salary , sum(s.salary) over(partition by sex ) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so |
# +------+------+-----+
# |1 |100 |600 |
# |4 |500 |600 |
# |2 |1000 |16000|
# |3 |10000 |16000|
# |5 |5000 |16000|
# +------+------+-----+
# 同组内连续求和
# sum(...) over( partition by... order by ... ),同第1点中的排序求和原理,只是范围限制在组内
SELECT s.emp_no, s.salary , sum(s.salary) over(partition by sex order by emp_no) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so |
# +------+------+-----+
# |1 |100 |100 |
# |4 |500 |600 |
# |2 |1000 |1000 |
# |3 |10000 |11000|
# |5 |5000 |16000|
# +------+------+-----+
# 即sex从大到小排列,sex组里各员工的薪水从高到低排列,累计和的规则不变(组内连续求和和 总体连续求和)。
SELECT
s.emp_no,
s.salary ,
sum(s.salary) over(partition by sex order by salary desc) as sex_gro_sum ,
sum(s.salary) over(order by salary desc) as all_sum
FROM salaries_60 s ;
# +------+------+-----------+-------+
# |emp_no|salary|sex_gro_sum|all_sum|
# +------+------+-----------+-------+
# |3 |10000 |10000 |10000 |
# |5 |5000 |15000 |15000 |
# |2 |1000 |16000 |16000 |
# |4 |500 |500 |16500 |
# |1 |100 |600 |16600 |
# +------+------+-----------+-------+
SELECT
s.emp_no,
s.salary ,
sum(s.salary) over(partition by sex order by emp_no asc ,salary desc) as sex_gro_sum ,
sum(s.salary) over(order by emp_no asc ,salary desc ) as all_sum
FROM salaries_60 s ;
# +------+------+-----------+-------+
# |emp_no|salary|sex_gro_sum|all_sum|
# +------+------+-----------+-------+
# |1 |100 |100 |100 |
# |2 |1000 |1000 |1100 |
# |3 |10000 |11000 |11100 |
# |4 |500 |600 |11600 |
# |5 |5000 |16000 |16600 |
# +------+------+-----------+-------+
1.五星(重要:思路很好)
# 23 (五星)对所有员工的当前(to_date='9999-01-01')薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
select * from salaries_18 ;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# |4 |1000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
# dense_rank 函数的考察
select emp_no, salary, dense_rank() over (order by salary desc ,emp_no asc)
from salaries_18;
# +------+------+----------------------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc ,emp_no asc)|
# +------+------+----------------------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |2 |
# |4 |1000 |3 |
# |1 |100 |4 |
# +------+------+----------------------------------------------------+
select emp_no, salary, dense_rank() over (order by salary desc)
from salaries_18 order by emp_no asc;
# +------+------+----------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc)|
# +------+------+----------------------------------------+
# |1 |100 |3 |
# |2 |1000 |2 |
# |3 |10000 |1 |
# |4 |1000 |2 |
# +------+------+----------------------------------------+
select emp_no, salary, dense_rank() over (order by salary desc)
from salaries_18 ;
# +------+------+----------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc)|
# +------+------+----------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |2 |
# |4 |1000 |2 |
# |1 |100 |3 |
# +------+------+----------------------------------------+
#
select emp_no, salary, dense_rank() over (order by salary desc ,emp_no asc)
from salaries_18;
# +------+------+----------------------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc ,emp_no asc)|
# +------+------+----------------------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |2 |
# |4 |1000 |3 |
# |1 |100 |4 |
# +------+------+----------------------------------------------------+
# 正确答案:
select s.emp_no,
salary,
dense_rank() over (order by salary desc) as rk
from salaries_18 s
where to_date = '9999-01-01'
# dense_rank() 定义了 rk 是降序的
order by rk, emp_no asc ;
# or
select emp_no, salary, dense_rank() over (order by salary desc)
from salaries_18 order by salary desc,emp_no asc;
# +------+------+----------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc)|
# +------+------+----------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |2 |
# |4 |1000 |2 |
# |1 |100 |3 |
# +------+------+----------------------------------------+
# 错误答案:
select emp_no, salary, dense_rank() over (order by salary desc ,emp_no asc)
from salaries_18 order by salary desc;
# +------+------+----------------------------------------------------+
# |emp_no|salary|dense_rank() over (order by salary desc ,emp_no asc)|
# +------+------+----------------------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |2 |
# |4 |1000 |3 |
# |1 |100 |4 |
# +------+------+----------------------------------------------------+
# 思路二 : 子查询添加一列 (重要思路:count 除了统计数据,还可以做排名处理:)
# num 为 1,count 为 0 ;
# 实现dense_rank()和rank() 在于 是否加 distinct
select emp_no,
salary,
(select count(distinct salary)
from salaries_18 s2
where s2.salary >= s1.salary)
from salaries_18 s1
order by s1.salary desc, s1.emp_no asc;
# 说明:
# 对于 salary 为 10000 这条数据, 过滤条件 s2.salary >= s1.salary--> s2.salary >= 10000,在 s2 找出大于等于 10000 的数据个数(这个个数叫做排名)
#对于 salary 为 1000 这条数据,过滤条件 s2.salary >= s1.salary--> s2.salary >= 1000,在 s2 找出大于等于 1000 的数据个数(这个个数叫做排名)
# 1000有两条,1000 有一条,count 为3,正确结果是 2 ,所以要去重
# 对于 salary 为 100 这条数据,过滤条件 s2.salary >= s1.salary--> s2.salary >= 100,在 s2 找出大于等于 1000 的数据个数(这个个数叫做排名)
#,100有一条, 1000有两条,1000 有一条,count 为4,正确结果为 3,,所以要去重
# 上述语句不去重之后的结果
select emp_no,
salary,
(select count(salary)
from salaries_18 s2
where s2.salary >= s1.salary)
from salaries_18 s1
order by s1.salary desc, s1.emp_no asc;
# +------+------+-------------------------------------------------------------------------------------------+
# |emp_no|salary|(select count(salary) |
# | | |from salaries_18 s2 |
# | | |where s2.salary >= s1.salary) |
# +------+------+-------------------------------------------------------------------------------------------+
# |3 |10000 |1 |
# |2 |1000 |3 |
# |4 |1000 |3 |
# |1 |100 |4 |
# +------+------+-------------------------------------------------------------------------------------------+
1.(四星):子查询的位置(select 后面作为一个新的列;from 后面作为表;where (子查询)> 10 作为判断条件) ;where id = (子查询)子查询获取库里数据
# 题目描述
# 28
# 查找描述信息(film.description)中包含robot的电影对应的分类名称(category.name)以及电影数目(count(film.film_id)),
# 而且还需要该分类包含电影总数量(count(film_category.category_id))>=5部
CREATE TABLE IF NOT EXISTS film_28 (
film_id smallint(5) NOT NULL DEFAULT '0',
title varchar(255) NOT NULL,
description text,
PRIMARY KEY (film_id));
CREATE TABLE category_28 (
category_id tinyint(3) NOT NULL ,
name varchar(25) NOT NULL, `last_update` timestamp,
PRIMARY KEY ( category_id ));
CREATE TABLE film_category_28 (
film_id smallint(5) NOT NULL,
category_id tinyint(3) NOT NULL, `last_update` timestamp);
INSERT INTO film_28 VALUES(1,'ACADEMY DINOSAUR','A Epic Drama of a Feminist And a Mad Scientist who must Battle a Teacher in The Canadian Rockies');
INSERT INTO film_28 VALUES(2,'ACE GOLDFINGER','A Astounding Epistle of a Database Administrator And a Explorer who must Find a Car in Ancient China');
INSERT INTO film_28 VALUES(3,'ADAPTATION HOLES','A Astounding Reflection of a Lumberjack And a Car who must Sink a Lumberjack in A Baloon Factory');
INSERT INTO film_28 VALUES(4,'AFFAIR PREJUDICE','A Fanciful Documentary of a Frisbee And a Lumberjack who must Chase a Monkey in A Shark Tank');
INSERT INTO film_28 VALUES(5,'AFRICAN EGG','A Fast-Paced Documentary of a Pastry Chef And a Dentist who must Pursue a Forensic Psychologist in The Gulf of Mexico');
INSERT INTO film_28 VALUES(6,'AGENT TRUMAN','A Intrepid Panorama of a robot And a Boy who must Escape a Sumo Wrestler in Ancient China');
INSERT INTO film_28 VALUES(7,'AIRPLANE SIERRA','A Touching Saga of a Hunter And a Butler who must Discover a Butler in A Jet Boat');
INSERT INTO film_28 VALUES(8,'AIRPORT POLLOCK','A Epic Tale of a Moose And a Girl who must Confront a Monkey in Ancient India');
INSERT INTO film_28 VALUES(9,'ALABAMA DEVIL','A Thoughtful Panorama of a Database Administrator And a Mad Scientist who must Outgun a Mad Scientist in A Jet Boat');
INSERT INTO film_28 VALUES(10,'ALADDIN CALENDAR','A Action-Packed Tale of a Man And a Lumberjack who must Reach a Feminist in Ancient China');
INSERT INTO category_28 VALUES(1,'Action','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(2,'Animation','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(3,'Children','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(4,'Classics','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(5,'Comedy','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(6,'Documentary','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(7,'Drama','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(8,'Family','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(9,'Foreign','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(10,'Games','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(11,'Horror','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(12,'Music','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(13,'New','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(14,'Sci-Fi','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(15,'Sports','2006-02-14 20:46:27');
INSERT INTO category_28 VALUES(16,'Travel','2006-02-14 20:46:27');
INSERT INTO film_category_28 VALUES(1,6,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(2,11,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(3,6,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(4,11,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(5,6,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(6,6,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(7,5,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(8,6,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(9,11,'2006-02-14 21:07:09');
INSERT INTO film_category_28 VALUES(10,15,'2006-02-14 21:07:09');
# 查找描述信息(film.description)中包含robot的电影对应的分类名称(category.name)以及电影数目(count(film.film_id)),
# 而且还需要该分类包含电影总数量(count(film_category.category_id))>=5部
# 思路一: 三表关联 ,该分类包含电影总数量(count(film_category.category_id))>=5 作为子查询来实现
select f.description, c.name, count(f.film_id)
from film_28 f
inner join film_category_28 fmc on f.film_id = fmc.film_id and f.description like '%robot%'
inner join category_28 c on fmc.category_id = c.category_id
where (select count(*) from film_category_28 fmc2 where fmc2.category_id = c.category_id) >= 5
group by f.description, c.name ;
# or (子查询作为 where后的条件)
select f.description, c.name, count(f.film_id)
from film_28 f,
film_category_28 fmc,
category_28 c
where f.film_id = fmc.film_id
and f.description like '%robot%'
and fmc.category_id = c.category_id
and (select count(*) from film_category_28 fmc2 where fmc2.category_id = c.category_id) >= 5
group by f.description, c.name;
# +-----------------------------------------------------------------------------------------+-----------+----------------+
# |description |name |count(f.film_id)|
# +-----------------------------------------------------------------------------------------+-----------+----------------+
# |A Intrepid Panorama of a robot And a Boy who must Escape a Sumo Wrestler in Ancient China|Documentary|1 |
# +-----------------------------------------------------------------------------------------+-----------+----------------+
# or
select *
from (
select f.description,
c.name,
count(f.film_id),
(select count(*) from film_category_28 fmc2 where fmc2.category_id = c.category_id) as ct
from film_28 f,
film_category_28 fmc,
category_28 c
where f.film_id = fmc.film_id
and f.description like '%robot%'
and fmc.category_id = c.category_id
group by f.description, c.name ,ct
) A
where ct >= 5;
# +-----------------------------------------------------------------------------------------+-----------+----------------+--+
# |description |name |count(f.film_id)|ct|
# +-----------------------------------------------------------------------------------------+-----------+----------------+--+
# |A Intrepid Panorama of a robot And a Boy who must Escape a Sumo Wrestler in Ancient China|Documentary|1 |5 |
# +-----------------------------------------------------------------------------------------+-----------+----------------+--+
# 思路二:先找出 该分类包含电影总数量(count(film_category.category_id))>=5部 的分类id集合 作为 category_id 的过滤条件
# # 查找描述信息(film.description)中包含robot的电影对应的分类名称(category.name)以及电影数目(count(film.film_id)),
# # 而且还需要该分类包含电影总数量(count(film_category.category_id))>=5部
select c.name,count(c.name) from category_28 c join film_category_28 fc
on c.category_id=fc.category_id
join film_28 f on f.film_id=fc.film_id
where f.description like '%robot%'
and fc.category_id in (SELECT category_id FROM film_category_28 GROUP BY category_id HAVING count(film_id)>=5)
GROUP BY c.name ;
1.
# 25 获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date='9999-01-01',
# 结果第一列给出员工的emp_no,
# 第二列给出其manager的manager_no,
# 第三列给出该员工当前的薪水emp_salary,
# 第四列给该员工对应的manager当前的薪水manager_salary
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
# 说明:题干和网友答案的意思是 dept_emp 里有 员工和经理信息
select * from dept_emp_25;
# +------+-------+----------+----------+
# |emp_no|dept_no|from_date |to_date |
# +------+-------+----------+----------+
# |1 |1 |2021-01-23|2021-01-23|
# |2 |1 |2021-01-23|2021-01-23|
# |3 |2 |2021-01-23|2021-01-23|
# |4 |2 |2021-01-23|2021-01-23|
# |5 |2 |2021-01-23|2021-01-23|
# +------+-------+----------+----------+
select * from dept_manager_25;
# +-------+------+----------+----------+
# |dept_no|emp_no|from_date |to_date |
# +-------+------+----------+----------+
# |1 |2 |2021-01-23|2021-01-23|
# |2 |5 |2021-01-23|2021-01-23|
# +-------+------+----------+----------+
select * from salaries_25;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# |4 |500 |2021-01-23|9999-01-01|
# |5 |5000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
# 获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date='9999-01-01',
# 思路一: 从员工表找出非经理的员工 ②子查询 增加一列比较其部门的经理salary
# 所有员工(不包括经理)的薪资
select de.emp_no, de.dept_no
from dept_emp_25 de
inner join salaries_25 s on de.emp_no = s.emp_no
where emp_no not in (select emp_no from dept_manager_25) ;
# 子查询增加一列
select de.emp_no,
de.dept_no,
(
select count(1)
from salaries_25 s2
where s2.emp_no = (select dm.emp_no from dept_manager_25 dm where dm.dept_no = de.dept_no)
# 当前部门下经理薪资要低于 该员工薪资
and s2.salary < s.salary
) as cot
from dept_emp_25 de
inner join salaries_25 s on de.emp_no = s.emp_no
where emp_no not in (select emp_no from dept_manager_25)
and cot > 0 ;
# 上述sql 识别不了 别名,加个套
select *
from (
select de.emp_no,
de.dept_no,
(
select count(1)
from salaries_25 s2
where s2.emp_no = (select dm.emp_no from dept_manager_25 dm where dm.dept_no = de.dept_no)
# 当前部门下经理薪资要高于 该员工薪资
and s2.salary < s.salary
) as cot
from dept_emp_25 de
inner join salaries_25 s on de.emp_no = s.emp_no
where de.emp_no not in (select dm2.emp_no from dept_manager_25 dm2)
) A
where cot > 0;
# +------+-------+---+
# |emp_no|dept_no|cot|
# +------+-------+---+
# |3 |2 |1 |
# +------+-------+---+
# 思路二: 从员工表找出非经理的员工 ②exists 和部门经理薪资比较(exists取交集,not exists 取差集 )(结果有问题:什么原因?待定)
select de.emp_no, de.dept_no
from dept_emp_25 de
inner join salaries_25 s on de.emp_no = s.emp_no
where de.emp_no not in (select emp_no from dept_manager_25)
and exists(
select count(*)
from salaries_25 ms2
# 通过传入 基层员工的 的部门编号,从 dept_manager_25 获得部门经理的 emp_no,进而拿到 部门经理的salary--->s2
where ms2.emp_no = (select dm.emp_no from dept_manager_25 dm where dm.dept_no = de.dept_no)
# 当前部门下经理薪资要低于 该员工薪资
and ms2.salary < s.salary
);
#
# 思路三:因为要比较的两个字段不在一列(想办法弄到一列: 员工薪资表和经理薪资表关联)
# **查询员工当前工资表 emp_sal**
select de.emp_no,de.dept_no,s1.salary as emp_salary
from dept_emp de,salaries s1
where de.emp_no=s1.emp_no
and s1.to_date='9999-01-01'
and de.to_date='9999-01-01'
# **查询经理当前工资表mag_sal**
select dm.emp_no as manager_no,dm.dept_no,s2.salary as manager_salary
from dept_manager dm,salaries s2
where dm.emp_no=s2.emp_no
and s2.to_date='9999-01-01'
and dm.to_date='9999-01-01'
# **联结表emp_sal和表mag_sal,连接条件部门编号相等,要求:员工工资>经理工资**
select emp_sal.emp_no,mag_sal.manager_no,
emp_sal.emp_salary,mag_sal.manager_salary
from (
select de.emp_no,de.dept_no,s1.salary as emp_salary
from dept_emp de,salaries s1
where de.emp_no=s1.emp_no
and s1.to_date='9999-01-01'
and de.to_date='9999-01-01'
)as emp_sal
inner join(
select dm.emp_no as manager_no,dm.dept_no,s2.salary as manager_salary
from dept_manager dm,salaries s2
where dm.emp_no=s2.emp_no
and s2.to_date='9999-01-01'
and dm.to_date='9999-01-01'
)as mag_sal
on emp_sal.dept_no=mag_sal.dept_no
where mag_sal.manager_salary<emp_sal.emp_salary;
# or
select de.emp_no,dm.emp_no as manager_no,
s1.salary as emp_salary,s2.salary as manager_salary
from dept_emp de,dept_manager dm,salaries s1,salaries s2
where de.dept_no=dm.dept_no
and de.emp_no=s1.emp_no
and dm.emp_no=s2.emp_no
and s1.salary>s2.salary
and s2.to_date='9999-01-01'
and s1.to_date='9999-01-01';
1. exists 和 not exists 的区别
exists 是取得交集, not exists 取得是差集
# 57 使用含有关键字exists查找未分配具体部门的员工的所有信息(一般)
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
select * from employees_57;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |皝 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|慕容 |垂 |女 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from dept_emp_57;
# |emp_no|dept_no|from_date |to_date |
# +------+-------+----------+----------+
# |1 |1 |2021-01-23|2021-01-23|
# |2 |2 |2021-01-23|2021-01-23|
# +------+-------+----------+----------+
# 使用含有关键字exists查找未分配具体部门的员工的所有信息
# 分析:employees_57 的每条数据去匹配dept_emp_57 ,取得是不存在的(3,4),not exists
select *
from employees_57
where not exists(select 1 from dept_emp_57 where employees_57.emp_no = dept_emp_57.emp_no);
# 查询结果:
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|慕容 |垂 |女 |2021-01-23|
# +------+----------+----------+---------+------+----------+
# 思路二: 左连接 条件是 is null
# 取交集
select *
from employees_57
where exists(select 1 from dept_emp_57 where employees_57.emp_no = dept_emp_57.emp_no);
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |皝 |男 |2021-01-23|
# +------+----------+----------+---------+------+----------+
1. (五星)exists 的用法
# 56 获取所有员工的emp_no、部门编号dept_no以及对应的bonus类型btype和received,
# 没有分配奖金的员工不显示对应的bonus类型btype和received
select * from employees_56;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |垂 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|峰 |萧 |男 |2021-01-23|
# |5 |2021-01-23|远山 |萧 |男 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from dept_emp_56;
# +------+-------+----------+----------+
# |emp_no|dept_no|from_date |to_date |
# +------+-------+----------+----------+
# |1 |1 |2021-01-23|2021-01-23|
# |2 |1 |2021-01-23|2021-01-23|
# |3 |2 |2021-01-23|2021-01-23|
# |4 |2 |2021-01-23|2021-01-23|
# |5 |2 |2021-01-23|2021-01-23|
# +------+-------+----------+----------+
select * from emp_bonus_56;
# +------+-------------------+-----+
# |emp_no|received |btype|
# +------+-------------------+-----+
# |1 |2010-01-01 00:00:00|1 |
# |2 |2010-01-01 00:00:00|2 |
# |3 |2010-01-01 00:00:00|3 |
# |4 |2010-01-01 00:00:00|4 |
# 获取所有员工的emp_no、部门编号dept_no以及对应的bonus类型btype和received,没有分配奖金的员工不显示对应的bonus类型btype和received
# 思路一: 左连接
select e.emp_no, d.dept_no, b.btype, b.received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
left join emp_bonus_56 b
on e.emp_no = b.emp_no;
# +------+-------+-----+-------------------+
# |emp_no|dept_no|btype|received |
# +------+-------+-----+-------------------+
# |1 |1 |1 |2010-01-01 00:00:00|
# |2 |1 |2 |2010-01-01 00:00:00|
# |3 |2 |3 |2010-01-01 00:00:00|
# |4 |2 |4 |2010-01-01 00:00:00|
# |5 |2 |NULL |NULL |
# +------+-------+-----+-------------------+
# 修改为:不显示null
select e.emp_no, d.dept_no, ifnull(b.btype,'') , ifnull(b.received,'')
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
left join emp_bonus_56 b
on e.emp_no = b.emp_no;
# +------+-------+------------------+---------------------+
# |emp_no|dept_no|ifnull(b.btype,'')|ifnull(b.received,'')|
# +------+-------+------------------+---------------------+
# |1 |1 |1 |2010-01-01 00:00:00 |
# |2 |1 |2 |2010-01-01 00:00:00 |
# |3 |2 |3 |2010-01-01 00:00:00 |
# |4 |2 |4 |2010-01-01 00:00:00 |
# |5 |2 | | |
# +------+-------+------------------+---------------------+
# 思路二:交集 + 差集(not exists)
select e.emp_no, d.dept_no, ifnull(b.btype, '') as btype, ifnull(b.received, '') as received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
inner join emp_bonus_56 b
on e.emp_no = b.emp_no
union all
select e.emp_no, d.dept_no, '' as btype, '' as received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where not exists(
# count(*) ,count(b.btype) 都是错误写法,直接 写个 1 就行
select 1 from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+-------+-----+-------------------+
# |emp_no|dept_no|btype|received |
# +------+-------+-----+-------------------+
# |1 |1 |1 |2010-01-01 00:00:00|
# |2 |1 |2 |2010-01-01 00:00:00|
# |3 |2 |3 |2010-01-01 00:00:00|
# |4 |2 |4 |2010-01-01 00:00:00|
# |5 |2 | | |
# +------+-------+-----+-------------------+
# 错误sql 如下
select e.emp_no, d.dept_no, '' as btype, '' as received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where exists(
# count(*) 会将 null 的也带出来,也就是 exists 失效
select count(*) from emp_bonus_56 b where b.emp_no = e.emp_no
);
# 结果 是 多了 第五条
# +------+-------+-----+--------+
# |emp_no|dept_no|btype|received|
# +------+-------+-----+--------+
# |1 |1 | | |
# |2 |1 | | |
# |3 |2 | | |
# |4 |2 | | |
# |5 |2 | | |
# +------+-------+-----+--------+
select e.emp_no, d.dept_no, '' as btype, '' as received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where exists(
# count(*) 会将 null 的也带出来,也就是 exists 失效
select count(b.btype) from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+-------+-----+--------+
# |emp_no|dept_no|btype|received|
# +------+-------+-----+--------+
# |1 |1 | | |
# |2 |1 | | |
# |3 |2 | | |
# |4 |2 | | |
# |5 |2 | | |
# +------+-------+-----+--------+
select e.emp_no, d.dept_no, '' as btype, '' as received
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where exists(
# count(*) 会将 null 的也带出来,也就是 exists 失效
select b.btype from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+-------+-----+--------+
# |emp_no|dept_no|btype|received|
# +------+-------+-----+--------+
# |1 |1 | | |
# |2 |1 | | |
# |3 |2 | | |
# |4 |2 | | |
# +------+-------+-----+--------+
select e.emp_no
from employees_56 e
where exists(
select 1 from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+
# |emp_no|
# +------+
# |1 |
# |2 |
# |3 |
# |4 |
# +------+
# 以下两个sql 说明 count(*) 是会包括 null 结果的,所以要用 count(1) or count(列名)
select e.emp_no
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where exists(
select count(*) from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+
# |emp_no|
# +------+
# |1 |
# |2 |
# |3 |
# |4 |
# |5 |
# +------+
select e.emp_no
from employees_56 e
inner join dept_emp_56 d on e.emp_no = d.emp_no
where exists(
select 1 from emp_bonus_56 b where b.emp_no = e.emp_no
);
# +------+
# |emp_no|
# +------+
# |1 |
# |2 |
# |3 |
# |4 |
# +------+
2. 低于最大,小于最小(范围查询)
# 18 查找当前薪水(to_date='9999-01-01')排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,你可以不使用order by完成吗
CREATE TABLE `employees_18` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries_18` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
select * from employees_18 ;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |垂 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from salaries_18;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
# 查找当前薪水(to_date='9999-01-01')排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,你可以不使用order by完成吗
# 只有三条数据的时候可以这么写(排除掉最大最小的就是中间的)
select e.emp_no, s.salary, e.last_name, e.first_name
from employees_18 e
inner join salaries_18 s
on e.emp_no = s.emp_no
where salary not in (select max(s2.salary) from salaries_18 s2 union all select min(s2.salary) from salaries_18 s2);
# +------+------+---------+----------+
# |emp_no|salary|last_name|first_name|
# +------+------+---------+----------+
# |2 |1000 |垂 |慕容 |
# +------+------+---------+----------+
# 思路2 dense_rank, oby 是别名,加个套才能识别这个别名(即:用 oby = 2 过滤数据)
# rank(),rownum() 也要类似的功能
select emp_no ,salary ,last_name, first_name
from (
select e.emp_no, s.salary, e.last_name, e.first_name, dense_rank() over (order by s.salary desc) as oby
from employees_18 e
inner join salaries_18 s
on e.emp_no = s.emp_no
) A
where oby = 2;
# +------+------+---------+----------+---+
# |emp_no|salary|last_name|first_name|oby|
# +------+------+---------+----------+---+
# |2 |1000 |垂 |慕容 |2 |
# +------+------+---------+----------+---+
# 思路三 排除掉最大的最大值
with tmp_second_saray as (
select e.emp_no, s.salary, e.last_name, e.first_name
from employees_18 e
inner join salaries_18 s
on e.emp_no = s.emp_no
where salary < (select max(salary) from salaries_18)
)
select * from tmp_second_saray where salary = (select max(salary) from tmp_second_saray) ;
# +------+------+---------+----------+
# |emp_no|salary|last_name|first_name|
# +------+------+---------+----------+
# |2 |1000 |垂 |慕容 |
# +------+------+---------+----------+
# 思路三的另一种写法
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
select e.emp_no, s.salary, e.last_name, e.first_name,max(s.salary)
from employees_18 e
inner join salaries_18 s
on e.emp_no = s.emp_no
# 过滤掉最大的后,老二就是最大的
where salary < (select max(salary) from salaries_18);
# +------+------+---------+----------+-------------+
# |emp_no|salary|last_name|first_name|max(s.salary)|
# +------+------+---------+----------+-------------+
# |1 |100 |一光 |郭 |1000 |
# +------+------+---------+----------+-------------+
3.
# 24 (一般) 获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date='9999-01-01'
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `dept_manager_24` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
select * from employees_24 ;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |垂 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|峰 |萧 |男 |2021-01-23|
# |5 |2021-01-23|远山 |萧 |男 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from dept_emp_24 ;
# +------+-------+----------+----------+
# |emp_no|dept_no|from_date |to_date |
# +------+-------+----------+----------+
# |1 |1 |2021-01-23|2021-01-23|
# |2 |2 |2021-01-23|2021-01-23|
# +------+-------+----------+----------+
select * from dept_manager_24 ;
# +-------+------+----------+----------+
# |dept_no|emp_no|from_date |to_date |
# +-------+------+----------+----------+
# |1 |4 |2021-01-23|2021-01-23|
# |2 |5 |2021-01-23|2021-01-23|
# +-------+------+----------+----------+
select * from salaries_24 ;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# |4 |1000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
# (这个不是标准答案)获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date='9999-01-01'
# 给了很多无用表
select de.dept_no ,de.emp_no ,s.salary from dept_emp_24 de inner join salaries_24 s on de.emp_no = s.emp_no;
# 标准答案(这个思路是 employees既有员工又有经理 )
SELECT de.dept_no,de.emp_no,s.salary
FROM dept_emp de
JOIN employees e
ON de.emp_no=e.emp_no
JOIN salaries s
ON de.emp_no=s.emp_no
WHERE de.emp_no NOT IN(
SELECT emp_no
FROM dept_manager_24
)
AND s.to_date='9999-01-01';
1. case when then end 的用法
# 59 给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。
# bonus类型btype为1其奖金为薪水salary的10%,
# btype为2其奖金为薪水的20%,其他类型均为薪水的30%。 当前薪水表示to_date='9999-01-01'
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
create table emp_bonus(
emp_no int not null,
received datetime not null,
btype smallint not null);
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));
select * from employees_59 ;
# +------+----------+----------+---------+------+----------+
# |emp_no|birth_date|first_name|last_name|gender|hire_date |
# +------+----------+----------+---------+------+----------+
# |1 |2021-01-23|郭 |一光 |男 |2021-01-23|
# |2 |2021-01-23|慕容 |垂 |男 |2021-01-23|
# |3 |2021-01-23|慕容 |雪 |女 |2021-01-23|
# |4 |2021-01-23|峰 |萧 |男 |2021-01-23|
# |5 |2021-01-23|远山 |萧 |男 |2021-01-23|
# +------+----------+----------+---------+------+----------+
select * from dept_emp_59 ;
# +------+-------+----------+----------+
# |emp_no|dept_no|from_date |to_date |
# +------+-------+----------+----------+
# |1 |1 |2021-01-23|2021-01-23|
# |2 |1 |2021-01-23|2021-01-23|
# |3 |2 |2021-01-23|2021-01-23|
# |4 |2 |2021-01-23|2021-01-23|
# |5 |2 |2021-01-23|2021-01-23|
# +------+-------+----------+----------+
select * from salaries_59 ;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date |
# +------+------+----------+----------+
# |1 |100 |2021-01-23|9999-01-01|
# |2 |1000 |2021-01-23|9999-01-01|
# |3 |10000 |2021-01-23|9999-01-01|
# |4 |500 |2021-01-23|9999-01-01|
# |5 |5000 |2021-01-23|9999-01-01|
# +------+------+----------+----------+
select * from emp_bonus_59 ;
# +------+-------------------+-----+
# |emp_no|received |btype|
# +------+-------------------+-----+
# |1 |2021-01-23 00:00:00|1 |
# |2 |2021-01-23 00:00:00|2 |
# |3 |2021-01-23 00:00:00|3 |
# +------+-------------------+-----+
# 给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。
# bonus类型btype为1其奖金为薪水salary的10%,
# btype为2其奖金为薪水的20%,其他类型均为薪水的30%。 当前薪水表示to_date='9999-01-01'
# 思路一: case when then
select e.emp_no, e.first_name, e.last_name, s.salary, b.btype,
case b.btype when b.btype=1 then s.salary*1.1
when b.btype=2 then s.salary*1.2
else s.salary*1.3
end as bs
from employees_59 e
left join salaries_59 s on e.emp_no = s.emp_no
left join emp_bonus_59 b on e.emp_no = b.emp_no ;
# or (省去括号里的条件)
select e.emp_no, e.first_name, e.last_name, s.salary, b.btype,
case b.btype when 1 then s.salary*1.1
when 2 then s.salary*1.2
else s.salary*1.3
end as bs
from employees_59 e
left join salaries_59 s on e.emp_no = s.emp_no
left join emp_bonus_59 b on e.emp_no = b.emp_no ;
# +------+----------+---------+------+-----+-------+
# |emp_no|first_name|last_name|salary|btype|bs |
# +------+----------+---------+------+-----+-------+
# |1 |郭 |一光 |100 |1 |110.0 |
# |2 |慕容 |垂 |1000 |2 |1200.0 |
# |3 |慕容 |雪 |10000 |3 |13000.0|
# |4 |峰 |萧 |500 |NULL |650.0 |
# |5 |远山 |萧 |5000 |NULL |6500.0 |
# +------+----------+---------+------+-----+-------+
# 参考答案:(这个参考答案有问题,没有列出全部数据来)
select e.emp_no,e.first_name,e.last_name,eb.btype,sa.salary,
(case eb.btype
when 1 then sa.salary*0.1
when 2 then sa.salary*0.2
else sa.salary*0.3 end) as bonus
from employees_59 e inner join emp_bonus_59 eb on e.emp_no=eb.emp_no
inner join salaries_59 sa on e.emp_no =sa.emp_no
where sa.to_date='9999-01-01' ;
# +------+----------+---------+-----+------+------+
# |emp_no|first_name|last_name|btype|salary|bonus |
# +------+----------+---------+-----+------+------+
# |1 |郭 |一光 |1 |100 |10.0 |
# |2 |慕容 |垂 |2 |1000 |200.0 |
# |3 |慕容 |雪 |3 |10000 |3000.0|
# +------+----------+---------+-----+------+------+
# 牛客
# 69 需求 描述
# 请你写出一个sql语句查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字,并且查询结果按照user的name升序排序
# 涉及到三张表 login_69 user_69 client_69
select * from login_69 ;
# +--+-------+---------+----------+
# |id|user_id|client_id|date |
# +--+-------+---------+----------+
# |1 |2 |1 |2021-02-12|
# |2 |3 |2 |2021-02-12|
# |3 |2 |2 |2021-02-13|
# |4 |3 |2 |2021-02-13|
# +--+-------+---------+----------+
select * from user_69 ;
# +--+--------+
# |id|name |
# +--+--------+
# |1 |tm |
# |2 |fh |
# |3 |wangchao|
# +--+--------+
select * from client_69 ;
# +--+-------+
# |id|name |
# +--+-------+
# |1 |pc |
# |2 |ios |
# |3 |android|
# |4 |h5 |
# +--+-------+
# 标准答案 1
# 三表直接关联可以获取所有登录记录,最后根据 user_id 分组,分组后获取 最大的登录日期就是符合要求的数据
select t2.name as u_n,t3.name as c_n,max(t1.date)as d
from login t1 join user t2 on t1.user_id=t2.id
join client t3 on t1.client_id =t3.id
group by t1.user_id
order by t2.name ;
# 标准答案 2
# having 分组后,作为组内数据筛选
select u.name,c.name,l.date
from login l
inner join user u
on l.user_id = u.id
inner join client c
on l.client_id=c.id
group by l.user_id
having l.date = max(l.date)
order by u.name asc;
# 思路 ① select 后的 子查询 login_69 ;user_69 ; client_69 ;
# 请你写出一个sql语句查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字,并且查询结果按照user的name升序排序
select l.*,
u.name ,
(select max(date) from login_69 l2 where l2.user_id = u.id)
from login_69 l
inner join user_69 u on l.user_id = u.id;
# +--+-------+---------+----------+--------+-----------------------------------------------------------+
# |id|user_id|client_id|date |name |(select max(date) from login_69 l2 where l2.user_id = u.id)|
# +--+-------+---------+----------+--------+-----------------------------------------------------------+
# |1 |2 |1 |2021-02-12|fh |2021-02-13 |
# |2 |3 |2 |2021-02-12|wangchao|2021-02-13 |
# |3 |2 |2 |2021-02-13|fh |2021-02-13 |
# |4 |3 |2 |2021-02-13|wangchao|2021-02-13 |
# +--+-------+---------+----------+--------+-----------------------------------------------------------+
select
u.name,
(select max(date) from login_69 l2 where l2.user_id = u.id) as dat,
(select distinct c.name
from login_69 l3
inner join client_69 c on l3.client_id = c.id
# 解决子查询返回多条数据
where l3.user_id = u.id limit 1) as name
from login_69 l
inner join user_69 u on l.user_id = u.id;
# +--------+----------+----+
# |name |dat |name|
# +--------+----------+----+
# |fh |2021-02-13|pc |
# |wangchao|2021-02-13|ios |
# |fh |2021-02-13|pc |
# |wangchao|2021-02-13|ios |
# +--------+----------+----+
# 去重
select
u.name as uName,
(select max(date) from login_69 l2 where l2.user_id = u.id) as dat,
(select distinct c.name
from login_69 l3
inner join client_69 c on l3.client_id = c.id
# 解决子查询返回多条数据
where l3.user_id = u.id limit 1) as name
from login_69 l
inner join user_69 u on l.user_id = u.id group by uName ;
# +--------+----------+----+
# |uName |dat |name|
# +--------+----------+----+
# |fh |2021-02-13|pc |
# |wangchao|2021-02-13|ios |
# +--------+----------+----+
# 测试 分组
select
u.name as uName,
(select max(date) from login_69 l2 where l2.user_id = u.id) as dat,
(select distinct c.name
from login_69 l3
inner join client_69 c on l3.client_id = c.id
# 解决子查询返回多条数据
where l3.user_id = u.id limit 1) as name
from login_69 l
inner join user_69 u on l.user_id = u.id group by uName ,name;
# +--------+----------+----+
# |uName |dat |name|
# +--------+----------+----+
# |fh |2021-02-13|pc |
# |wangchao|2021-02-13|ios |
# +--------+----------+----+
# 思路② select 子查询,直接追加 每个user最近的时间
select u.name as uName,
c.name as cName ,
(select max(date) from login_69 l3 where l3.user_id = l.user_id group by l3.user_id) as lastDate
from user_69 u
inner join login_69 l on u.id = l.user_id
inner join client_69 c on c.id = l.client_id group by uName;
# +--------+-----+----------+
# |uName |cName|lastDate |
# +--------+-----+----------+
# |fh |pc |2021-02-13|
# |wangchao|ios |2021-02-13|
# +--------+-----+----------+
# 思路 ② exists
# 请你写出一个sql语句查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字,并且查询结果按照user的name升序排序
# login_69 ;user_69 ; client_69 ;
select u.name as uName,
c.name as cName,
l.date as lastDate
from user_69 u
inner join login_69 l on u.id = l.user_id
inner join client_69 c on c.id = l.client_id
where exists(select 1
from login_69 as l2
where l2.user_id = l.user_id
# 获取每个用户最近登录日期(最大的登录日期)
and l.date = (select max(date) from login_69 l3 where l3.user_id = l.user_id group by l3.user_id));
# +--------+-----+----------+
# |uName |cName|lastDate |
# +--------+-----+----------+
# |fh |ios |2021-02-13|
# |wangchao|ios |2021-02-13|
# +--------+-----+----------+
# 68
# 需求:牛客每天有很多人登录,请你统计一下牛客每个日期登录新用户个数
select * from login_69;
# +--+-------+---------+----------+
# |id|user_id|client_id|date |
# +--+-------+---------+----------+
# |1 |2 |1 |2021-02-11|
# |2 |3 |2 |2021-02-12|
# |3 |2 |2 |2021-02-13|
# |4 |3 |2 |2021-02-13|
# |5 |4 |2 |2021-02-14|
# |6 |4 |2 |2021-02-15|
# |7 |5 |2 |2021-02-16|
# |8 |5 |2 |2021-02-17|
# |9 |6 |2 |2021-02-18|
# |10|6 |2 |2021-02-18|
# |11|7 |2 |2021-02-19|
# +--+-------+---------+----------+
# 需求:牛客每天有很多人登录,请你统计一下牛客每个日期登录新用户个数
# 思路① 当天新用户,时间小于 当天是没有数据的,所以就找当天的新数据
select date,
(select count(*) from login_69 where date < ll.date ) as A,
(select count(*) from login_69 where date <= ll.date) as B,
( select (B - A)) as result
from login_69 ll
group by date;
# 加个套
select date,(B - A) as result from (
select date,
(select count(*) from login_69 where date < ll.date ) as A,
(select count(*) from login_69 where date <= ll.date) as B
from login_69 ll
group by date
) As AA;
# +----------+------+
# |date |result|
# +----------+------+
# |2021-02-11|1 |
# |2021-02-12|1 |
# |2021-02-13|2 |
# |2021-02-14|1 |
# |2021-02-15|1 |
# |2021-02-16|1 |
# |2021-02-17|1 |
# |2021-02-18|2 |
# |2021-02-19|1 |
# +----------+------+
# 灵感来源 从 a,b,c,d 中找到有多少个 逗号
# select length('a,b,c,d') - ( select length(replace('a,b,c,d',',',''))) ;
# +----------------------------------------------------------------+
# |length('a,b,c,d') - ( select length(replace('a,b,c,d',',','')))|
# +----------------------------------------------------------------+
# |3 |
# +----------------------------------------------------------------+
# 推荐SQL:
select a.date,
sum(case
when rank=1
then 1
else 0
end) new
from (select date, row_number()
over(partition by user_id order by date) as rank
from login_69) as a
group by date;