学习链接:http://datawhale.club/t/topic/471
答案参考1:https://shimo.im/docs/5xkGMKv9pNSgoR3X/read
答案参考2:http://datawhale.club/t/topic/993
练习一: 各部门工资最高的员工(难度:中等)
创建Employee 表,包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。
mysql> create table Employee(
-> Id integer not null,
-> Name varchar(124) not null,
-> Salary integer not null,
-> DepartmentId integer not null,
-> primary key(Id));
插入数据
mysql> insert into Employee values(1,'Joe',70000,1);
mysql> insert into Employee values(2, 'Henry',80000,2);
mysql> insert into Employee values(3 ,'Sam',60000,2);
mysql> insert into Employee values(4, 'Max',90000,1);
创建Department 表,包含公司所有部门的信息。
mysql> create table Department(
-> Id integer not null,
-> Name varchar(124) not null,
-> primary key(Id));
插入数据
mysql> insert into Department values(1,'IT');
mysql> insert into Department values(2,'Sales');
解答
思路:先按照部门查询出最大Salary,然后通过join语句匹配部门信息。
mysql> select D.Name as Department,
-> E.Name as Employee,
-> E.Salary
-> from Department as D
-> join Employee as E
-> on E.DepartmentId = D.Id
-> and E.Salary = (select max(Salary) from Employee where DepartmentId = D.Id);
练习二: 换座位(难度:中等)
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。
其中纵列的id是连续递增的,小美想改变相邻俩学生的座位。你能不能帮她写一个 SQL query 来输出小美想要的结果呢?注意:如果学生人数是奇数,则不需要改变最后一个同学的座位。
请创建如下所示seat表:
mysql> create table seat(
-> id integer not null,
-> student varchar(124) not null,
-> primary key(id));
mysql> insert into seat values(1,'Abbot');
mysql> insert into seat values(2,'Doris');
mysql> insert into seat values(3,'Emerson');
mysql> insert into seat values(4,'Green');
mysql> insert into seat values(5,'Jeames');
解答:方法1
从最后结果来看就是 :①id为偶数的需要往前挪②id为奇数的需要往后挪③再考虑最后一位是奇数还是偶数,奇数不变(发现偶数的情况已经包含在前面了)通过 id%2=1 来判断是奇数。这里需要注意的地方是,最后一位,我们在判断奇数往后挪的时候,是不包含最后一位的。所以在②的时候要限定 id 小于总个数。
mysql> select * from(
-> select id-1 as id, student
-> from seat where id%2=0
-> union
-> select id+1 as id, student
-> from seat where id%2=1
-> and (id+1) <= (select count(*) from seat)
-> union
-> select id as id, student
-> from seat where id%2=1
-> and (id+1) > (select count(*) from seat)
-> ) as rank1
-> order by id asc;
解答:方法2
使用case表达式,使用函数Mod()求余数
mysql> select case
-> when id = (select max(id) from seat) then id
-> when mod(id,2)=1 then id+1
-> when mod(id,2)=0 then id-1
-> else NULL end as id,
-> student
-> from seat
-> order by id;
练习三: 分数排名(难度:中等)
编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。
创建以下Scores表:
mysql> create table Scores(
-> Id int not null,
-> Score real not null,
-> primary key(Id));
mysql> insert into Scores values(1,3.50);
mysql> insert into Scores values(2,3.65);
mysql> insert into Scores values(3,4.00);
mysql> insert into Scores values(4,3.85);
mysql> insert into Scores values(5,4.00);
mysql> insert into Scores values(6,3.65);
根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列)
解答:方法1
解题思路:-- 考察窗口函数
-- PARTITION BY 用于分组,不分组时,省略即可
-- ORDER BY 用于排序,默认升级,降序使用 DESC关键字
-- RANK() 美式排名,跳过式排序,得分相同时排序相同
-- DENSE_RANK() 中式排名, 递增式排序,得分相同时排序相同
-- ROW_NUMBER() 赋予唯一连续的名次
mysql> select Score,
-> dense_rank() over (order by Score desc) as rank1
-> from scores;
方法2
解题思路:然后就有个取巧的办法。我们还是需要获取去重的分数清单,但是不需要排名ID了:① 原始表里的最大值。 分数清单只取最大值,然后COUNT(),这样就只有1② 原始表里的第二大值。 分数清单取最大值和第二大值,然后COUNT(), 这样就是2...以此内推。在code里就是 分数清单里的score要大于等于原始表的某个具体的值。这个写法的缺点是,每一个原始表socre列的值,都需要重新扫描去重后的分数清单,在数据表大的情况下,性能会很差。
mysql> select Score,
-> (select count(distinct Score) from scores
-> where Score >= S.Score) as rank1
-> from scores as S
-> order by Score desc;
练习四:连续出现的数字(难度:中等)
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。
mysql> create table logs(
-> Id int not null,
-> Num int not null,
-> primary key(Id));
mysql> insert into logs values(1,1);
mysql> insert into logs values(2,1);
mysql> insert into logs values(3,1);
mysql> insert into logs values(4,2);
mysql> insert into logs values(5,1);
mysql> insert into logs values(6,2);
mysql> insert into logs values(7,2);
给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。
解答:方法1
思路:全连接三张Logs表,分别用来搜索连续三个数中的第一、二、三个数,检查是否相同。并且最后还要排除重复。
mysql> select distinct l1.Num as ConsecutiveNums
-> from logs as l1, logs as l2, logs as l3
-> where l1.Num = l2.Num
-> and l2.Num = l3.Num
-> and l1.Id = l2.Id-1
-> and l2.Id = l3.Id-1;
练习五:树节点 (难度:中等)
对于tree表,id是树节点的标识,p_id是其父节点的id。
每个节点都是以下三种类型中的一种:
- Root: 如果节点是根节点。
- Leaf: 如果节点是叶子节点。
-
Inner: 如果节点既不是根节点也不是叶子节点。
写一条查询语句打印节点id及对应的节点类型。按照节点id排序。上面例子的对应结果为:
说明
节点’1’是根节点,因为它的父节点为NULL,有’2’和’3’两个子节点。
节点’2’是内部节点,因为它的父节点是’1’,有子节点’4’和’5’。
节点’3’,‘4’,'5’是叶子节点,因为它们有父节点但没有子节点。
下面是树的图形:
注意:如果一个树只有一个节点,只需要输出根节点属性。
总体思路:对于一个节点,先判断是否为根节点。如果不是,再判断是内部节点还是叶子节点。如果一个节点的父节点id为空(null),则为根节点;对于一个不是根节点的节点,如果它是某些节点的父节点,则为内部节点,否则为叶子节点。
解答:方法1
mysql> select id,
-> case when p_id is null then 'Root'
-> when id in (select p_id from tree) then 'Inner'
-> else 'Leaf' end as Type
-> from tree;
解答:方法2
mysql> select id,
-> if(isnull(p_id), 'Root',
-> if(id in (select p_id from tree), 'Inner','Leaf')) as Type
-> from tree
-> order by id;
练习六:至少有五名直接下属的经理 (难度:中等)
Employee表包含所有员工及其上级的信息。每位员工都有一个Id,并且还有一个对应主管的Id(ManagerId)。
mysql> create table employee(
-> id int,
-> name varchar(20),
-> department varchar(20),
-> managerid int,
-> primary key(id));
mysql> insert into employee values(101,'John','A',null);
mysql> insert into employee values(102,'Dan','A',101);
mysql> insert into employee values(103,'James','A',101);
mysql> insert into employee values(104,'Amy','A',101);
mysql> insert into employee values(105,'Ron','B',101);
解答:方法1
#可以根据ManagerId分组,找出有5个下属的主管的Id:
mysql> select managerid from employee
-> group by managerid
-> having count(*) >= 5;
#然后输出对应的姓名
mysql> select name from employee
-> where id in (
-> select managerid from employee
-> group by managerid
-> having count(*) >= 5);
方法2
连接两张相同的表,按照条件进行查询
mysql> select m.name
-> from employee as e
-> join employee as m
-> on e.managerid=m.id
-> group by m.name
-> having count(e.name) >= 5;