题目一
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。
其中纵列的 id 是连续递增的
小美想改变相邻俩学生的座位。
你能不能帮她写一个 SQL query 来输出小美想要的结果呢?
示例:
±--------±--------+
| id | student |
±--------±--------+
| 1 | Abbot |
| 2 | Doris |
| 3 | Emerson |
| 4 | Green |
| 5 | Jeames |
±--------±--------+
假如数据输入的是上表,则输出结果如下:
±--------±--------+
| id | student |
±--------±--------+
| 1 | Doris |
| 2 | Abbot |
| 3 | Green |
| 4 | Emerson |
| 5 | Jeames |
±--------±--------+
注意:
如果学生人数是奇数,则不需要改变最后一个同学的座位。
SELECT if(id%2=1,
if(id=@a,
id,
id+1),
id-1) id,
student
FROM seat s,
(SELECT @a:=count(id)
FROM seat) ret
ORDER BY id;
题目二
在 Facebook 或者 Twitter 这样的社交应用中,人们经常会发好友申请也会收到其他人的好友申请。
表 request_accepted 存储了所有好友申请通过的数据记录,其中, requester_id 和 accepter_id 都是用户的编号。
| requester_id | accepter_id | accept_date|
|--------------|-------------|------------|
| 1 | 2 | 2016_06-03 |
| 1 | 3 | 2016-06-08 |
| 2 | 3 | 2016-06-08 |
| 3 | 4 | 2016-06-09 |
写一个查询语句,求出谁拥有最多的好友和他拥有的好友数目。对于上面的样例数据,结果为:
| id | num |
|----|-----|
| 3 | 3 |
注意:
保证拥有最多好友数目的只有 1 个人。
好友申请只会被接受一次,所以不会有 requester_id 和 accepter_id 值都相同的重复记录。
解释:
编号为 ‘3’ 的人是编号为 ‘1’,‘2’ 和 ‘4’ 的好友,所以他总共有 3 个好友,比其他人都多。
进阶:
在真实世界里,可能会有多个人拥有好友数相同且最多,你能找到所有这些人吗?
SELECT id AS id,
ucnt AS num
FROM
(SELECT id,
count(1) AS ucnt
FROM
(SELECT requester_id AS id
FROM request_accepted
UNION
allSELECT accepter_id AS id
FROM request_accepted )
GROUP BY id
ORDER BY ucnt DESC )
WHERE rownum = 1
题目三
表 point_2d 保存了所有点(多于 2 个点)的坐标 (x,y) ,这些点在平面上两两不重合。
写一个查询语句找到两点之间的最近距离,保留 2 位小数。
| x | y |
|----|----|
| -1 | -1 |
| 0 | 0 |
| -1 | -2 |
最近距离在点 (-1,-1) 和(-1,2) 之间,距离为 1.00 。所以输出应该为:
| shortest |
|----------|
| 1.00 |
注意:任意点之间的最远距离小于 10000 。
SELECT ROUND(MIN(shortest),
2) shortest FROM
(SELECT POWER(POWER((p1.x-p2.x),
2)+POWER((p1.y-p2.y),
2),
0.5) shortest
FROM point_2d p1,point_2d p2
WHERE p1.x !=p2.x
OR p1.y != p2.y )a
题目四
编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。
±—±------+
| Id | Score |
±—±------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
±—±------+
例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):
±------±-----+
| Score | Rank |
±------±-----+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 2 |
| 3.65 | 3 |
| 3.65 | 3 |
| 3.50 | 4 |
±------±-----+
SELECT a.Score,
sum(case
WHEN b.Score>=a.Score THEN
1 end) AS Rank
FROM Scores a,
(SELECT DISTINCT Score
FROM Scores ) b
GROUP BY a.id
ORDER BY a.Score desc;
题目五
给定一个表 tree,id 是树节点的编号, p_id 是它父节点的 id 。
±—±-----+
| id | p_id |
±—±-----+
| 1 | null |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
±—±-----+
树中每个节点属于以下三种类型之一:
叶子:如果这个节点没有任何孩子节点。
根:如果这个节点是整棵树的根,即没有父节点。
内部节点:如果这个节点既不是叶子节点也不是根节点。
写一个查询语句,输出所有节点的编号和节点的类型,并将结果按照节点编号排序。上面样例的结果为:
±—±-----+
| id | Type |
±—±-----+
| 1 | Root |
| 2 | Inner|
| 3 | Leaf |
| 4 | Leaf |
| 5 | Leaf |
±—±-----+
解释
节点 ‘1’ 是根节点,因为它的父节点是 NULL ,同时它有孩子节点 ‘2’ 和 ‘3’ 。
节点 ‘2’ 是内部节点,因为它有父节点 ‘1’ ,也有孩子节点 ‘4’ 和 ‘5’ 。
节点 ‘3’, ‘4’ 和 ‘5’ 都是叶子节点,因为它们都有父节点同时没有孩子节点。
样例中树的形态如下:
1
/ \
2 3
/ \
4 5
SELECT DISTINCT t1.id,
(CASE
WHEN t1.p_id IS NULL THEN
'Root' ELSE
CASE
WHEN t2.id IS NULL THEN
'Leaf'
ELSE 'Inner'
END END) AS Type
FROM tree t1
LEFT JOIN tree t2
ON t1.id=t2.p_id
题目六
Employee 表包含所有员工和他们的经理。每个员工都有一个 Id,并且还有一列是经理的 Id。
±-----±---------±----------±---------+
|Id |Name |Department |ManagerId |
±-----±---------±----------±---------+
|101 |John |A |null |
|102 |Dan |A |101 |
|103 |James |A |101 |
|104 |Amy |A |101 |
|105 |Anne |A |101 |
|106 |Ron |B |101 |
±-----±---------±----------±---------+
给定 Employee 表,请编写一个SQL查询来查找至少有5名直接下属的经理。对于上表,您的SQL查询应该返回:
±------+
| Name |
±------+
| John |
±------+
SELECT b.Name
FROM employee a
INNER JOIN employee b
ON a.managerid= b.id
GROUP BY a.managerid
HAVING count(*)>=5;
题目七
一所大学有 2 个数据表,分别是 student 和 department ,这两个表保存着每个专业的学生数据和院系数据。
写一个查询语句,查询 department 表中每个专业的学生人数 (即使没有学生的专业也需列出)。
将你的查询结果按照学生人数降序排列。 如果有两个或两个以上专业有相同的学生数目,将这些部门按照部门名字的字典序从小到大排列。
student 表格如下:
| Column Name | Type |
|--------------|-----------|
| student_id | Integer |
| student_name | String |
| gender | Character |
| dept_id | Integer |
其中, student_id 是学生的学号, student_name 是学生的姓名, gender 是学生的性别, dept_id 是学生所属专业的专业编号。
department 表格如下:
| Column Name | Type |
|-------------|---------|
| dept_id | Integer |
| dept_name | String |
dept_id 是专业编号, dept_name 是专业名字。
这里是一个示例输入:
student 表格:
| student_id | student_name | gender | dept_id |
|------------|--------------|--------|---------|
| 1 | Jack | M | 1 |
| 2 | Jane | F | 1 |
| 3 | Mark | M | 2 |
department 表格:
| dept_id | dept_name |
|---------|-------------|
| 1 | Engineering |
| 2 | Science |
| 3 | Law |
示例输出为:
| dept_name | student_number |
|-------------|----------------|
| Engineering | 2 |
| Science | 1 |
| Law | 0 |
SELECT dept_name,
(case
WHEN student_num is NULL THEN
0
ELSE student_num end) student_number
FROM
(SELECT dept_id,
count(*) student_num
FROM student
GROUP BY dept_id) t1
RIGHT JOIN department t2
ON t1.dept_id=t2.dept_id
ORDER BY student_number desc,dept_name asc
题目八
写一个查询语句,将 2016 年 (TIV_2016) 所有成功投资的金额加起来,保留 2 位小数。
对于一个投保人,他在 2016 年成功投资的条件是:
他在 2015 年的投保额 (TIV_2015) 至少跟一个其他投保人在 2015 年的投保额相同。
他所在的城市必须与其他投保人都不同(也就是说维度和经度不能跟其他任何一个投保人完全相同)。
输入格式:
表 insurance 格式如下:
| Column Name | Type |
|-------------|---------------|
| PID | INTEGER(11) |
| TIV_2015 | NUMERIC(15,2) |
| TIV_2016 | NUMERIC(15,2) |
| LAT | NUMERIC(5,2) |
| LON | NUMERIC(5,2) |
PID 字段是投保人的投保编号, TIV_2015 是该投保人在2015年的总投保金额, TIV_2016 是该投保人在2016年的投保金额, LAT 是投保人所在城市的维度, LON 是投保人所在城市的经度。
样例输入
| PID | TIV_2015 | TIV_2016 | LAT | LON |
|-----|----------|----------|-----|-----|
| 1 | 10 | 5 | 10 | 10 |
| 2 | 20 | 20 | 20 | 20 |
| 3 | 10 | 30 | 20 | 20 |
| 4 | 10 | 40 | 40 | 40 |
样例输出
| TIV_2016 |
|----------|
| 45.00 |
解释
就如最后一个投保人,第一个投保人同时满足两个条件:
1. 他在 2015 年的投保金额 TIV_2015 为 ‘10’ ,与第三个和第四个投保人在 2015 年的投保金额相同。
2. 他所在城市的经纬度是独一无二的。
第二个投保人两个条件都不满足。他在 2015 年的投资 TIV_2015 与其他任何投保人都不相同。
且他所在城市的经纬度与第三个投保人相同。基于同样的原因,第三个投保人投资失败。
所以返回的结果是第一个投保人和最后一个投保人的 TIV_2016 之和,结果是 45 。
SELECT sum(case
WHEN t3.TIV_2015 is NOT NULL THEN
t2.TIV_2016
ELSE 0 end) TIV_2016 from
(SELECT TIV_2015
FROM insurance
GROUP BY TIV_2015
HAVING count(TIV_2015)>1)t3
RIGHT JOIN
(SELECT *
FROM insurance
GROUP BY LAT, LON
HAVING count(LAT) = 1
AND count(LON) = 1) t2
ON t2.TIV_2015=t3.TIV_2015
题目九
Customer 表:
±------------±--------+
| Column Name | Type |
±------------±--------+
| customer_id | int |
| product_key | int |
±------------±--------+
product_key 是 Product 表的外键。
Product 表:
±------------±--------+
| Column Name | Type |
±------------±--------+
| product_key | int |
±------------±--------+
product_key 是这张表的主键。
写一条 SQL 查询语句,从 Customer 表中查询购买了 Product 表中所有产品的客户的 id。
示例:
Customer 表:
±------------±------------+
| customer_id | product_key |
±------------±------------+
| 1 | 5 |
| 2 | 6 |
| 3 | 5 |
| 3 | 6 |
| 1 | 6 |
±------------±------------+
Product 表:
±------------+
| product_key |
±------------+
| 5 |
| 6 |
±------------+
Result 表:
±------------+
| customer_id |
±------------+
| 1 |
| 3 |
±------------+
购买了所有产品(5 和 6)的客户的 id 是 1 和 3 。
SELECT customer_id
FROM
(SELECT DISTINCT c.customer_id,
c.product_key AS cpk,
p.product_key AS ppk
FROM customer c
LEFT JOIN product p
ON c.product_key=p.product_key) tmp
GROUP BY customer_id
HAVING COUNT(cpk) =
(SELECT COUNT(product_key)
FROM product)
AND COUNT(ppk) =
(SELECT COUNT(product_key)
FROM product)
题目十
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
±—±----+
| Id | Num |
±—±----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
±—±----+
例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。
±----------------+
| ConsecutiveNums |
±----------------+
| 1 |
±----------------+
SELECT DISTINCT l1.Num AS ConsecutiveNums
FROM Logs l1, Logs l2, Logs l3
WHERE l1.Id = l2.Id - 1
AND l2.Id = l3.Id - 1
AND l1.Num = l2.Num
AND l2.Num = l3.Num ;
题目十一
表: Candidate
±----±--------+
| id | Name |
±----±--------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
±----±--------+
表: Vote
±----±-------------+
| id | CandidateId |
±----±-------------+
| 1 | 2 |
| 2 | 4 |
| 3 | 3 |
| 4 | 2 |
| 5 | 5 |
±----±-------------+
id 是自动递增的主键,
CandidateId 是 Candidate 表中的 id.
请编写 sql 语句来找到当选者的名字,上面的例子将返回当选者 B.
±-----+
| Name |
±-----+
| B |
±-----+
注意:
你可以假设没有平局,换言之,最多只有一位当选者。
elect t3.name Name
FROM Candidate t3,
(SELECT t1.CandidateId CandidateId
FROM Vote t1
GROUP BY t1.CandidateId
ORDER BY count(*) DESC limit 1)t2
WHERE t3.id=t2.CandidateId
题目十二
Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。
±—±------±-------±-------------+
| Id | Name | Salary | DepartmentId |
±—±------±-------±-------------+
| 1 | Joe | 70000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
±—±------±-------±-------------+
Department 表包含公司所有部门的信息。
±—±---------+
| Id | Name |
±—±---------+
| 1 | IT |
| 2 | Sales |
±—±---------+
编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。
±-----------±---------±-------+
| Department | Employee | Salary |
±-----------±---------±-------+
| IT | Max | 90000 |
| Sales | Henry | 80000 |
±-----------±---------±-------+
SELECT Department.name AS 'Department', Employee.name AS 'Employee', Salary
FROM Employee
JOIN Department
ON Employee.DepartmentId = Department.Id
WHERE (Employee.DepartmentId , Salary) IN
(SELECT DepartmentId,
MAX(Salary)
FROM Employee
GROUP BY DepartmentId ) ;
题目十三
从 survey_log 表中获得回答率最高的问题,survey_log 表包含这些列:uid, action, question_id, answer_id, q_num, timestamp。
uid 表示用户 id;action 有以下几种值:“show”,“answer”,“skip”;当 action 值为 “answer” 时 answer_id 非空,而 action 值为 “show” 或者 “skip” 时 answer_id 为空;q_num 表示当前会话中问题的编号。
请编写SQL查询来找到具有最高回答率的问题。
示例:
输入:
±-----±----------±-------------±-----------±----------±-----------+
| uid | action | question_id | answer_id | q_num | timestamp |
±-----±----------±-------------±-----------±----------±-----------+
| 5 | show | 285 | null | 1 | 123 |
| 5 | answer | 285 | 124124 | 1 | 124 |
| 5 | show | 369 | null | 2 | 125 |
| 5 | skip | 369 | null | 2 | 126 |
±-----±----------±-------------±-----------±----------±-----------+
输出:
±------------+
| survey_log |
±------------+
| 285 |
±------------+
解释:
问题285的回答率为 1/1,而问题369回答率为 0/1,因此输出285。
注意: 回答率最高的含义是:同一问题编号中回答数占显示数的比例。
SELECT question_id AS survey_log
FROM survey_log
GROUP BY question_id
ORDER BY ( ifnull( count( answer_id ), 0 ) / count( * ) ) DESC LIMIT 1
题目十四
在 facebook 中,表 follow 会有 2 个字段: followee, follower ,分别表示被关注者和关注者。
请写一个 sql 查询语句,对每一个关注者,查询他的关注者数目。
比方说:
±------------±-----------+
| followee | follower |
±------------±-----------+
| A | B |
| B | C |
| B | D |
| D | E |
±------------±-----------+
应该输出:
±------------±-----------+
| follower | num |
±------------±-----------+
| B | 2 |
| D | 1 |
±------------±-----------+
解释:
B 和 D 都在在 follower 字段中出现,作为被关注者,B 被 C 和 D 关注,D 被 E 关注。A 不在 follower 字段内,所以A不在输出列表中。
注意:
被关注者永远不会被他 / 她自己关注。
将结果按照字典序返回。
SELECT f1.follower AS follower,
count(distinct f2.follower) AS num
FROM follow f1, follow f2
WHERE f1.follower = f2.followee
GROUP BY f1.follower
ORDER BY f1.follower