[SQL] MySQL查询_LeetCode

目录

一、第N高的薪水【中】

二、分数排名(实现dense_rank()排名函数)【中】

延伸:实现row_number()排名函数

延伸:实现rank()排名函数

三、部门工资前三高的员工【难】

四、体育馆的人流量【难】

五、行程和用户【难】

六、大的国家【易】

七、超过5名学生的课【易】

八、交换工资【易】

九、换座位【中】


一、第N高的薪水【中】

获取 Employee 表中第 高的薪水(Salary)。

Id    Salary
1100
2200
3300

如 n = 2 时,应返回第二高的薪水 200。如果不存在第 高的薪水,那么查询应返回 null。

getNthHighestSalary(2)
200

查询如下:

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    SET N = N - 1;
  RETURN (
  SELECT DISTINCT IFNULL(Salary, NULL) getNthHighestSalary
    FROM Employee
    ORDER BY Salary DESC
    LIMIT N,1
  );
END

二、分数排名(实现dense_rank()排名函数)【中】

如果两个分数相同,则两个分数排名相同。

平分后的下一个名次应该是下一个连续的整数值。也就是名次之间不应该有“间隔”。

Id    Score
13.50
23.65
34.00
43.85
54.00
63.65

例如,根据上述给定的 Scores 表,查询应该返回(按分数从高到低排列):

ScoreRank 
4.001
4.001
3.85

2

3.653
3.653
3.504

MySQL Server查询:(MySQL不适用dense_rank函数)

SELECT Score, (DENSE_RANK() OVER (ORDER BY Score DESC)) Rank
    FROM Scores;

MySQL查询:方法一:

SELECT s1.Score, COUNT(DISTINCT s2.Score) Rank
FROM Scores s1, Scores s2
WHERE s2.Score >= s1.Score
GROUP BY s1.Id, s1.Score
ORDER BY s1.Score DESC;

方法二:

SELECT a.Score, 
CONVERT((CASE
WHEN @score = a.Score THEN @rank 
WHEN @score := a.Score THEN @rank := @rank+1
WHEN @score = 0 THEN @rank := @rank+1
END), SIGNED) AS 'Rank' 
FROM (SELECT Score FROM Scores ORDER BY Score DESC) a, 
    (SELECT @score := NULL, @rank := 0) b;

说明:

  • 设定变量@score, @rank,并分别初始赋值NULL, 0,赋值为" := " ;

  • 指定排序列的值不变时,@rank的值不变;指定排序列的值变化时,@rank的值自增1;

  • 字符串数字转换:CAST('123' AS SIGNED) 或 CONVERT('123', SIGNED) 或 '123'+0【引申数字转字符串 CONCAT() 】;

  • 如果给定表分数为[0.0],则返回[0.0, 1],而不是[0.0, NULL];

  • 如果MySQL报错“ERROR 1064 (42000): You have an error in your SQL syntax;”,把“AS Rank”改为“AS 'Rank' "可解决。


延伸:实现row_number()排名函数

查询返回:

ScoreRank 
4.001
4.002
3.85

3

3.654
3.655
3.506

MySQL查询:

SELECT a.Score, @rank := @rank+1 AS 'Rank'
FROM (SELECT Score FROM Scores ORDER BY Score DESC) a, 
    (SELECT @rank := 0) b;

说明:

  • 设置变量@rank初始值为0,并逐行累加。


延伸:实现rank()排名函数

查询返回:

ScoreRank 
4.001
4.001
3.85

3

3.654
3.654
3.506

MySQL查询:

SELECT s.Score, s.Rank FROM(
SELECT a.Score, 
    IF(@score = a.Score, @rank, @rank := @inRank) AS 'Rank', 
        @inRank := @inRank+1, @score := a.Score 
FROM (SELECT Score FROM Scores ORDER BY Score DESC) a, 
    (SELECT @score := NULL, @rank := 0, @inRank := 1) b
) s;

说明:

  • 设定变量@score, @rank, @inRank,并分别初始赋值NULL, 0, 1;

  • 指定排序列的值不变时,@rank的值不变;指定排序列的值变化时,@rank的值跳变为@inRank内部计数的值;

  • @inRank变量每行自增1,用于内部计数。


三、部门工资前三高的员工【难】

Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id 。

Id          Name        Salary      DepartmentId
1Joe700001
2Henry800002
3Sam600002
4Max900001
5Janet690001
6Randy850001

Department 表包含公司所有部门的信息。

Id  Name
1IT
2Sales

编写一个 SQL 查询,找出每个部门工资前三高的员工。例如,根据上述给定的表格,查询结果应返回:

DepartmentEmployee  Salary    
ITMax90000
ITRandy85000
ITJoe70000
SalesHenry80000
SalesSam60000

MySQL查询:

SELECT Department.Name AS Department, e1.Name AS Employee, 
    e1.Salary AS Salary
FROM Employee e1
JOIN Department
ON e1.DepartmentId = Department.Id
WHERE 3 >= (
    SELECT COUNT(DISTINCT e2.Salary)
    FROM Employee e2
    WHERE e2.Salary >= e1.Salary 
	AND e1.DepartmentId = e2.DepartmentId)
ORDER BY Department.Name, e1.Salary DESC;
  •  

四、体育馆的人流量【难】

X 市建了一个新的体育馆,每日人流量信息被记录在这三列信息中:序号 (id)、日期 (date)、 人流量 (people)。

请编写一个查询语句,找出高峰期时段,要求连续三天及以上,并且每天人流量均不少于100。

例如,表 stadium

+------+------------+-----------+
| id   | date       | people    |
+------+------------+-----------+
| 1    | 2017-01-01 | 10        |
| 2    | 2017-01-02 | 109       |
| 3    | 2017-01-03 | 150       |
| 4    | 2017-01-04 | 99        |
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-08 | 188       |
+------+------------+-----------+

对于上面的示例数据,输出为:

+------+------------+-----------+
| id   | date       | people    |
+------+------------+-----------+
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-08 | 188       |
+------+------------+-----------+

Note:
每天只有一行记录,日期随着 id 的增加而增加。

SELECT DISTINCT s4.*
FROM stadium s1, stadium s2, stadium s3, stadium s4
WHERE s2.id = s1.id + 1
AND s3.id = s1.id + 2
AND s1.people >= 100
AND s2.people >= 100
AND s3.people >= 100
AND s4.id IN (s1.id, s2.id, s3.id);
  •  

五、行程和用户【难】

Trips 表中存所有出租车的行程信息。每段行程有唯一键 Id,Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。

+----+-----------+-----------+---------+--------------------+----------+
| Id | Client_Id | Driver_Id | City_Id |        Status      |Request_at|
+----+-----------+-----------+---------+--------------------+----------+
| 1  |     1     |    10     |    1    |     completed      |2013-10-01|
| 2  |     2     |    11     |    1    | cancelled_by_driver|2013-10-01|
| 3  |     3     |    12     |    6    |     completed      |2013-10-01|
| 4  |     4     |    13     |    6    | cancelled_by_client|2013-10-01|
| 5  |     1     |    10     |    1    |     completed      |2013-10-02|
| 6  |     2     |    11     |    6    |     completed      |2013-10-02|
| 7  |     3     |    12     |    6    |     completed      |2013-10-02|
| 8  |     2     |    12     |    12   |     completed      |2013-10-03|
| 9  |     3     |    10     |    12   |     completed      |2013-10-03| 
| 10 |     4     |    13     |    12   | cancelled_by_driver|2013-10-03|
+----+-----------+-----------+---------+--------------------+----------+

Users 表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。

+----------+--------+--------+
| Users_Id | Banned |  Role  |
+----------+--------+--------+
|    1     |   No   | client |
|    2     |   Yes  | client |
|    3     |   No   | client |
|    4     |   No   | client |
|    10    |   No   | driver |
|    11    |   No   | driver |
|    12    |   No   | driver |
|    13    |   No   | driver |
+----------+--------+--------+

写一段 SQL 语句查出 2013年10月1日 至 2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

+------------+-------------------+
|     Day    | Cancellation Rate |
+------------+-------------------+
| 2013-10-01 |       0.33        |
| 2013-10-02 |       0.00        |
| 2013-10-03 |       0.50        |
+------------+-------------------+
SELECT 
    tc.Request_at Day, 
    IFNULL(ROUND((SUM(tc.cancel)/ SUM(tc.num)),2),0) 
        AS 'Cancellation Rate'
FROM (
    SELECT t.Request_at, t.Status, COUNT(*) num, 
    IF((t.Status = 'cancelled_by_client' 
        OR t.Status = 'cancelled_by_driver'),COUNT(*), 0) cancel
    FROM (
        SELECT t1.Id, t1.Status, t1.Request_at
        FROM Trips t1
    INNER JOIN (SELECT * FROM Users 
                WHERE Role = 'client' AND Banned = 'No') uc
    INNER JOIN (SELECT * FROM Users 
                WHERE Role = 'driver' AND Banned = 'No') ud
    ON uc.Users_id = t1.Client_id
    AND ud.Users_id = t1.Driver_id
    ) t
    GROUP BY t.Request_at, Status
) tc
GROUP BY tc.Request_at
HAVING tc.Request_at BETWEEN '2013-10-01' and '2013-10-03'
  •  

六、大的国家【易】

如果一个国家的面积超过300万平方公里,或者人口超过2500万,那么这个国家就是大国家。

编写一个SQL查询,输出表中所有大国家的名称、人口和面积。

  • 注意:用UNION比用OR的运行时间快

七、超过5名学生的课【易】

有一个courses 表 ,有: student (学生) 和 class (课程)。列出所有超过或等于5名学生的课。

学生在每个课中不应被重复计算。

SELECT class 
    FROM courses
    GROUP BY class
    HAVING COUNT(DISTINCT student) >= 5;
  • 注意:(DISTINCT student)

八、交换工资【易】

给定一个 salary表,如下所示,有m=男性 和 f=女性的值 。交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求使用一个更新查询,并且没有中间临时表。

UPDATE salary SET sex = IF(sex = 'm','f','m');
  • 仅用IF函数即可。

九、换座位【中】

小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。

其中纵列的 id 是连续递增的,小美想改变相邻俩学生的座位。

示例:

+---------+---------+
|    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 DISTINCT s1.id id, (
    IF(s1.id % 2 = 1, s3.student, s2.student)
    ) student
    FROM seat s1, seat s2, seat s3
    WHERE (s1.id = s2.id + 1 AND s1.id = s3.id - 1) 
    OR (s1.id = 1 AND s1.id = s3.id - 1)
    OR (s1.id = (SELECT MAX(id) FROM seat) 
        AND s1.id % 2 = 0 AND s1.id = s2.id + 1) 
)
UNION 
(SELECT id, student FROM seat
    WHERE id = (SELECT MAX(id) FROM seat)
    AND EXISTS 
    (SELECT id FROM seat 
           WHERE id = (SELECT MAX(id) FROM seat) AND id % 2 = 1)
)
ORDER BY id;
  •  

完整数据库题目地址:https://leetcode-cn.com/problemset/database/

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值