MySQL LeetCode

175. 组合两个表

题目描述

表1: Person
+-------------+---------+
| 列名 | 类型 |
+-------------+---------+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
+-------------+---------+
PersonId 是上表主键 表2: Address
+-------------+---------+
| 列名 | 类型 |
+-------------+---------+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
+-------------+---------+
AddressId 是上表主键 编写一个 SQL 查询,满足条件:
无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息: FirstName, LastName, City, State
# 表架构

DROP TABLE IF EXISTS Person;
CREATE TABLE Person(
PersonId INT,
FirstName VARCHAR(255),
LastName VARCHAR(255)
); DROP TABLE IF EXISTS Address;
CREATE TABLE Address(
AddressId INT,
PersonId INT,
City VARCHAR(255),
State VARCHAR(255)
); INSERT INTO Person(
PersonId,
FirstName,
LastName
) VALUES (
1,
'Wang',
'Allen'
); INSERT INTO Address(
AddressId,
PersonId,
City,
State
) VALUES (
1,
2,
'New York City',
'New York'
);

错误的解法

SELECT P.FirstName, P.LastName, A.City, A.State
FROM Person AS P, Address AS A
WHERE P.PersonId = A.PersonId;

原因:没有考虑到 Person 表中的信息即使在 Address 表没有关联信息也要保留的情况。

左外联结解法

分析:对两个表进行联结操作时,考虑到 Person 表中的信息即使在 Address 表没有关联信息也要保留的情况,此时可以用左外联结,将 Person 表放在 LEFT JOIN 的左边。

SELECT FirstName, LastName, City, State
FROM Person AS P LEFT JOIN Address AS A ON P.PersonId = A.PersonId;

总结

多表的联结分为以下几种类型:

  • 左联结(left join),联结结果保留左表的全部数据
  • 右联结(right join),联结结果保留右表的全部数据
  • 内联结(inner join),取两表的公共数据

176. 第二高的薪水

题目描述

编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。
+----+--------+
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+ 例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null。 +---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+
# 表架构

DROP TABLE IF EXISTS Employee;
CREATE TABLE Employee(
Id INT,
Salary INT
);
INSERT INTO Employee(
Id,
Salary
) VALUES (
1,
100
), (
2,
200
), (
3,
300
);

子查询和 LIMIT 子句解法

分析:使用子查询和 LIMIT 子句,如果不存在第二高的薪水,为了返回 null ,可以利用临时表。

SELECT(
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1 # 从第1行开始取1行
) AS SecondHighestSalary;

IFNULL 函数解法

分析:解决 NULL 问题的另一种方法是使用 IFNULL 函数。

SELECT IFNULL(
(
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1 # 从第1行开始取1行
), NULL
) as SecondHighestSalary;

总结

MySQL中 ISNULLIFNULLNULLIF 的用法:

  • ISNULL(expr):如果 exprNULL,那么 ISNULL() 的返回值为 1,否则返回值为 0。值得注意的是, 使用 =NULL 值对比通常是错误的。
  • IFNULL(expr1, expr2):如果 expr1 不为 NULL,则 IFNULL() 的返回值为 expr1 ;否则其返回值为 expr2IFNULL() 的返回值是数字或是字符串,具体情况取决于其所使用的语境。
  • NULLIF(expr1, expr2):如果 expr1 = expr2 成立,那么返回值为 NULL,否则返回值为expr1。这和CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END相同。

LIMIT x OFFSET y; 等价于 LIMIT y, x; 即从第y行开始取x行。

177. 第N高的薪水

题目描述

编写一个 SQL 查询,获取 Employee 表中第 n 高的薪水(Salary)。

+----+--------+
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null。 +------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200 |
+------------------------+
# 表架构同 176. 第二高的薪水

IFNULL 函数解法

分析:单表不分组 TOPN 问题。解法同176. 第二高的薪水。

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
SET N := N-1;
RETURN (
# Write your MySQL query statement below.
SELECT IFNULL(
(SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET N # 等价于 LIMIT N, 1
), NULL)
);
END

总结

查看数据库函数:

SHOW FUNCTION STATUS;

创建一个函数:

CREATE FUNCTION test (id CHAR(100)) RETURN INTEGER;
BEGIN -- 开始
DECLARE temp INTEGER; -- 声明一个变量 SELECT COUNT(*) INTO temp -- 做查询语句,将结果赋值给temp
FROM demo_table
WHERE id = id; -- 给定条件 RETURN temp; -- 返回结果temp END -- 结束

在Linux下编写函数:

set global log_bin_trust_function_creators = 1; -- 开启 bin_log 复制函数创建
DELIMITER $$ -- 在Linux下要定义一个标识符 CREATE FUNCTION test (id CHAR(100)) RETURN INTEGER; -- 创建一个函数
BEGIN -- 开始
DECLARE temp INTEGER; -- 声明一个变量 SELECT COUNT(*) INTO temp -- 做查询语句,将结果赋值给temp
FROM demo_table
WHERE id = id; -- 给定条件 RETURN temp; -- 返回结果temp END $$ -- 后跟的是定义的标识符 DELIMITER $$ -- 结束

binlog 是MySQL数据库的二进制日志,用于记录用户对数据库操作的SQL语句(除了数据查询语句)信息。可以使用 mysqlbinlog 命令查看二进制日志的内容。

binlog 的格式有三种:

  • STATMENT模式:基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的SQL语句会记录到 binlog 中。

    • 优点:不需要记录每一条SQL语句与每行的数据变化,这样 binlog 的日志也会比较少,减少了磁盘IO,提高性能。
    • 缺点:在某些情况下会导致主从数据不一致。
  • ROW模式:基于行的复制(row-based replication, RBR),不记录每一条SQL语句的上下文信息,仅需记录哪条数据被修改,修改成什么样子。
    • 优点:不会出现主从数据不一致的情况。
    • 缺点:会产生大量的日志,尤其是ALTER TABLE的时候会让日志暴涨。
  • MIXED模式:混合模式复制(mixed-based replication, MBR),以上两种模式的混合使用,一般的复制使用STATEMENT模式保存 binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存 binlog,MySQL会根据执行的SQL语句选择日志保存方式。

无论是增量备份还是主从复制,都是需要开启 binlog 日志,最好跟数据目录设置到不同的磁盘分区,可以降低 IO 等待,提升性能;并且在磁盘故障的时候可以利用 binlog 恢复数据。

178. 分数排名

题目描述

编写一个 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 |
+-------+------+ 重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。例如 `Rank`
# 表架构
DROP TABLE IF EXISTS Scores;
CREATE TABLE Scores (Id int, Score DECIMAL(3,2));
insert into Scores (Id, Score) values ('1', '3.5');
insert into Scores (Id, Score) values ('2', '3.65');
insert into Scores (Id, Score) values ('3', '4.0');
insert into Scores (Id, Score) values ('4', '3.85');
insert into Scores (Id, Score) values ('5', '4.0');
insert into Scores (Id, Score) values ('6', '3.65');

数学解法

分析:先提取出大于等于X的所有分数集合H,将H去重后的元素个数就是X的排名。

SELECT A.Score AS Score,
(SELECT COUNT(DISTINCT B.Score)
FROM Scores AS B
WHERE B.Score >= A.Score) AS 'Rank'
FROM Scores AS A
ORDER BY A.Score DESC;

窗口函数解法

分析:涉及到排名问题,可以使用窗口函数。MySQL从8.0开始支持窗口函数。

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

总结

涉及到排名问题,可以使用窗口函数,非常高效简洁。

窗口函数,也叫 OLAP 函数(Online Anallytical Processing,联机分析处理),可以对数据库数据进行实时分析处理。

# 基本语法
<窗口函数> OVER (partition by <分组的名字>
order by <用于排序的列名>)

<窗口函数> 主要分为两种:

  • 专用窗口函数,如rank, dense_rank, row_number等。
  • 聚合函数,如max,sum等。

从语法可以知道,窗口函数有以下功能:

  • 同时具有分组(partition by)和排序(order by)的功能。
  • 不减少原表的行数,所以经常用来在每组内排名。

因为窗口函数是对where或者group by子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中

RANK()

SELECT * FROM Scores;

# 输出
+------+-------+
| Id | Score |
+------+-------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
+------+-------+ # 使用 RANK()
SELECT Id, Score, RANK() OVER (ORDER BY Score DESC) AS 'Rank'
FROM Scores; # 输出
+------+-------+------+
| Id | Score | Rank |
+------+-------+------+
| 3 | 4.00 | 1 |
| 5 | 4.00 | 1 |
| 4 | 3.85 | 3 |
| 2 | 3.65 | 4 |
| 6 | 3.65 | 4 |
| 1 | 3.50 | 6 |
+------+-------+------+

DENSE_RANK()

SELECT * FROM Scores;

# 输出
+------+-------+
| Id | Score |
+------+-------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
+------+-------+ # 使用 DENSE_RANK()
SELECT Id, Score, DENSE_RANK() OVER (ORDER BY Score DESC) AS 'Dense_Rank'
FROM Scores; # 输出
+------+-------+------------+
| Id | Score | Dense_Rank |
+------+-------+------------+
| 3 | 4.00 | 1 |
| 5 | 4.00 | 1 |
| 4 | 3.85 | 2 |
| 2 | 3.65 | 3 |
| 6 | 3.65 | 3 |
| 1 | 3.50 | 4 |
+------+-------+------------+

ROW_NUMBER()

SELECT * FROM Scores;

# 输出
+------+-------+
| Id | Score |
+------+-------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
+------+-------+ # 使用 ROW_NUMBER()
SELECT Id, Score, ROW_NUMBER() OVER (ORDER BY Score DESC) AS 'ROW_NUMBER'
FROM Scores; # 输出
+------+-------+------------+
| Id | Score | ROW_NUMBER |
+------+-------+------------+
| 3 | 4.00 | 1 |
| 5 | 4.00 | 2 |
| 4 | 3.85 | 3 |
| 2 | 3.65 | 4 |
| 6 | 3.65 | 5 |
| 1 | 3.50 | 6 |
+------+-------+------------+

聚合函数SUM()作为窗口函数

SELECT * FROM Scores;

# 输出
+------+-------+
| Id | Score |
+------+-------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
+------+-------+ # 使用 聚合函数SUM()作为窗口函数
SELECT Id, Score, SUM(Score) OVER (ORDER BY Score DESC) AS 'SumScore'
FROM Scores; # 输出
+------+-------+----------+
| Id | Score | SumScore |
+------+-------+----------+
| 3 | 4.00 | 8.00 |
| 5 | 4.00 | 8.00 |
| 4 | 3.85 | 11.85 |
| 2 | 3.65 | 19.15 |
| 6 | 3.65 | 19.15 |
| 1 | 3.50 | 22.65 |
+------+-------+----------+

180. 连续出现的数字

题目描述

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

+----+-----+
| Id | Num |
+----+-----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
+----+-----+
例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。 +-----------------+
| ConsecutiveNums |
+-----------------+
| 1 |
+-----------------+
# 表架构
DROP TABLE IF EXISTS Logs;
CREATE TABLE Logs(
Id INT, Num INT
); INSERT INTO Logs(
Id, Num
)VALUES (
1, 1
), (
2, 1
), (
3, 1
), (
4, 2
), (
5, 1
), (
6, 2
), (
7, 2
);

DISTINCTWHERE语句解法

分析:假设连续出现的意味着相同数字的 Id 是连着的,使用 Logs 并检查是否有 3 个连续的相同数字。显然,这种解法效率很低,而且题目好像有点牵强。

SELECT DISTINCT L1.Num AS ConsecutiveNums
FROM Logs AS L1, Logs AS L2, Logs AS L3
WHERE L1.Id = L2.Id - 1 AND L2.Id = L3.Id - 1 AND L1.Num = L2.Num AND L2.Num = L3.Num;

窗口函数解法

分析:使用窗口函数的偏差函数 LEAD()。理解为:将Num复制两列Num1和Num2,然后Num1整体向上移动一行,Num2整体向上移动两行。

SELECT DISTINCT Num as ConsecutiveNums
FROM (
SELECT Num, LEAD(Num, 1) OVER() AS Num1, LEAD(Num, 2) OVER() AS Num2
FROM Logs
) AS C
WHERE C.Num = C.Num1 AND C.Num1 = C.Num2;

总结

LEAD() 函数对于计算同一结果集中当前行和后续行之间的差异非常有用。

# 语法
LEAD(<expression>[,offset[, default_value]]) OVER (
PARTITION BY (expr)
ORDER BY (expr)
)

offset 是从当前行向前行的行数,以获取值。offset 必须是一个非负整数。如果offset 为零,则 LEAD() 函数计算 expression 当前行的值。如果省略 offset,则 LEAD() 函数默认使用一个。如果没有后续行,则 LEAD() 函数返回 default_value 。例如,如果 offset 是1,则最后一行的返回值为 default_value 。如果您未指定 default_value,则函数返回 NULL

# 查找每个客户的订单日期和下一个订单日期
SELECT
customerName,
orderDate,
LEAD(orderDate,1) OVER (
PARTITION BY customerNumber
ORDER BY orderDate ) nextOrderDate
FROM
orders
INNER JOIN customers USING (customerNumber); # 输出
+------------------------------------+------------+---------------+
| customerName | orderDate | nextOrderDate |
+------------------------------------+------------+---------------+
| Atelier graphique | 2013-05-20 | 2014-09-27 |
| Atelier graphique | 2014-09-27 | 2014-11-25 |
| Atelier graphique | 2014-11-25 | NULL |
| Signal Gift Stores | 2013-05-21 | 2014-08-06 |
| Signal Gift Stores | 2014-08-06 | 2014-11-29 |
| Signal Gift Stores | 2014-11-29 | NULL |
| Australian Collectors, Co. | 2013-04-29 | 2013-05-21 |
| Australian Collectors, Co. | 2013-05-21 | 2014-02-20 |
| Australian Collectors, Co. | 2014-02-20 | 2014-11-24 |
| Australian Collectors, Co. | 2014-11-24 | 2014-11-29 |
| Australian Collectors, Co. | 2014-11-29 | NULL |
| La Rochelle Gifts | 2014-07-23 | 2014-10-29 |
| La Rochelle Gifts | 2014-10-29 | 2015-02-03 |
| La Rochelle Gifts | 2015-02-03 | 2015-05-31 |
| La Rochelle Gifts | 2015-05-31 | NULL |
| Baane Mini Imports | 2013-01-29 | 2013-10-10 |
| Baane Mini Imports | 2013-10-10 | 2014-10-15 |
+------------------------------------+------------+---------------+

181. 超过经理收入的员工

题目描述

EmployeeA 表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+
给定 EmployeeA 表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。 +----------+
| EmployeeA |
+----------+
| Joe |
+----------+
# 表架构
Create table If Not Exists EmployeeA (Id int, Name varchar(255), Salary int, ManagerId int)
insert into EmployeeA
(Id, Name, Salary, ManagerId)
values
(1, 'Joe', 70000, 3),
(2, 'Henry', 80000, 4),
(3, 'Sam', 60000, NULL),
(4, 'Max', 90000, NULL);

WHERE语句解法

分析:WHERE语句中减少笛卡尔乘积的行数。

SELECT a.Name AS 'EmployeeA'
FROM EmployeeA AS a, EmployeeA AS b
WHERE a.ManagerId = b.Id AND a.Salary > b.Salary;

JOIN语句解法

SELECT a.Name AS 'EmployeeA'
FROM EmployeeA AS a INNER JOIN EmployeeA AS b ON a.ManagerId = b.Id AND a.Salary > b.Salary;

总结

这两个解法本质是一样的。但是用联结的效率更高点。

182. 查找重复的电子邮箱

题目描述

编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱。

示例:

+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
+----+---------+
根据以上输入,你的查询应返回以下结果: +---------+
| Email |
+---------+
| a@b.com |
+---------+
说明:所有电子邮箱都是小写字母。
# 表架构
Create table If Not Exists Person (Id int, Email varchar(255));
Truncate table Person;
insert into Person (Id, Email) values ('1', 'a@b.com');
insert into Person (Id, Email) values ('2', 'c@d.com');
insert into Person (Id, Email) values ('3', 'a@b.com');

GROUP BY 和临时表解法

分析:看到“找重复”的关键字眼,一般要用 group by 分组,因为 where 不能过滤分组,可以用一个临时表保存分组结果然后过滤。

select Email
from (
select Email, count(Email) as num
from Person
group by Email
) as tempTable
where num > 1;

GROUP BYHAVING 解法

分析where 过滤行,而 having 可以过滤分组。

select Email
from Person
group by Email
having count(Email) > 1;

总结

  • 必须要记住的优先顺序:where > group by > having > order by

  • where 子句无法与聚合函数一起使用。

  • 找出重复出现 n 次的数据,只需要改变 having 语句中的条件即可

select 列名
from 表名
group by 列名
having count(列名) > n;

183. 从不订购的客户

题目描述

某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。

Customers 表:

+----+-------+
| Id | Name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders 表: +----+------------+
| Id | CustomerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
例如给定上述表格,你的查询应返回: +-----------+
| Customers |
+-----------+
| Henry |
| Max |
+-----------+
# 表架构
Create table If Not Exists Customers (Id int, Name varchar(255));
Create table If Not Exists Orders (Id int, CustomerId int);
Truncate table Customers;
insert into Customers (Id, Name) values ('1', 'Joe');
insert into Customers (Id, Name) values ('2', 'Henry');
insert into Customers (Id, Name) values ('3', 'Sam');
insert into Customers (Id, Name) values ('4', 'Max');
Truncate table Orders;
insert into Orders (Id, CustomerId) values ('1', '3');
insert into Orders (Id, CustomerId) values ('2', '1');

常规排除解法

分析:常规解法。

select Name as Customers
from Customers
where Id not in (
select CustomerId
from Orders
);

左联结解法

分析:左联结很适合找在A中但不再B中的类型。

select c.Name as Customers
from Customers as c left join Orders as o on c.Id = o.CustomerId
where o.CustomerId is null;

总结

对于多表联结问题,一张图就够了,left join 意思是左边的全保留,如果左边的和右边的不符合 on 的条件,那么左边对应右边的列自动为 null

图出自《图解SQL面试题》

184. 部门工资最高的员工

题目描述

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

+----+-------+--------+--------------+
| Id | Name | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1 | Joe | 70000 | 1 |
| 2  | Jim   | 90000  | 1            |
| 3 | Henry | 80000 | 2 |
| 4 | Sam | 60000 | 2 |
| 5 | Max | 90000 | 1 |
+----+-------+--------+--------------+
Department 表包含公司所有部门的信息。 +----+----------+
| Id | Name |
+----+----------+
| 1 | IT |
| 2 | Sales |
+----+----------+
编写一个 SQL 查询,找出每个部门工资最高的员工。对于上述表,您的 SQL 查询应返回以下行(行的顺序无关紧要)。 +------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Max | 90000 |
| IT         | Jim      | 90000  |
| Sales | Henry | 80000 |
+------------+----------+--------+
解释: Max 和 Jim 在 IT 部门的工资都是最高的,Henry 在销售部的工资最高。
# 表架构
Create table If Not Exists Employee (Id int, Name varchar(255), Salary int, DepartmentId int);
Create table If Not Exists Department (Id int, Name varchar(255));
Truncate table Employee;
insert into Employee (Id, Name, Salary, DepartmentId) values (1, 'Joe', '70000', 1);
insert into Employee (Id, Name, Salary, DepartmentId) values (2, 'Jim', '90000', 1);
insert into Employee (Id, Name, Salary, DepartmentId) values (3, 'Henry', '80000', 2);
insert into Employee (Id, Name, Salary, DepartmentId) values (4, 'Sam', '60000', 2);
insert into Employee (Id, Name, Salary, DepartmentId) values (5, 'Max', '90000', 1);
Truncate table Department;
insert into Department (Id, Name) values (1, 'IT');
insert into Department (Id, Name) values (2, 'Sales');

内联结和 IN 解法

分析:先把两个表内联结,然后用 IN 语句查询结果是否在部门内最高工资的临时表中。

select d.name as 'Department', e.name as 'Employee', Salary
from Employee as e inner join Department as d on e.DepartmentId = d.Id
where (e.DepartmentId, Salary) in (
select DepartmentId, max(Salary)
from Employee
group by DepartmentId
);

总结

多用联结。

185. 部门工资前三高的所有员工

题目描述

Employee 表包含所有员工信息,每个员工有其对应的工号 Id,姓名 Name,工资 Salary 和部门编号 DepartmentId 。

+----+-------+--------+--------------+
| Id | Name | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1 | Joe | 85000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
| 5 | Janet | 69000 | 1 |
| 6 | Randy | 85000 | 1 |
| 7 | Will | 70000 | 1 |
+----+-------+--------+--------------+
Department 表包含公司所有部门的信息。 +----+----------+
| Id | Name |
+----+----------+
| 1 | IT |
| 2 | Sales |
+----+----------+
编写一个 SQL 查询,找出每个部门获得前三高工资的所有员工。例如,根据上述给定的表,查询结果应返回: +------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Max | 90000 |
| IT | Randy | 85000 |
| IT | Joe | 85000 |
| IT | Will | 70000 |
| Sales | Henry | 80000 |
| Sales | Sam | 60000 |
+------------+----------+--------+
解释: IT 部门中,Max 获得了最高的工资,Randy 和 Joe 都拿到了第二高的工资,Will 的工资排第三。销售部门(Sales)只有两名员工,Henry 的工资最高,Sam 的工资排第二。
# 表架构
Create table If Not Exists Employee (Id int, Name varchar(255), Salary int, DepartmentId int);
Create table If Not Exists Department (Id int, Name varchar(255));
Truncate table Employee;
insert into Employee (Id, Name, Salary, DepartmentId) values ('1', 'Joe', '85000', '1');
insert into Employee (Id, Name, Salary, DepartmentId) values ('2', 'Henry', '80000', '2');
insert into Employee (Id, Name, Salary, DepartmentId) values ('3', 'Sam', '60000', '2');
insert into Employee (Id, Name, Salary, DepartmentId) values ('4', 'Max', '90000', '1');
insert into Employee (Id, Name, Salary, DepartmentId) values ('5', 'Janet', '69000', '1');
insert into Employee (Id, Name, Salary, DepartmentId) values ('6', 'Randy', '85000', '1');
insert into Employee (Id, Name, Salary, DepartmentId) values ('7', 'Will', '70000', '1');
Truncate table Department;
insert into Department (Id, Name) values ('1', 'IT');
insert into Department (Id, Name) values ('2', 'Sales');

窗口函数解法

分析: 多表分组 TOPN 问题。每组最大的N条记录。这类问题涉及到“既要分组,又要排序”的情况,要能想到用窗口函数来实现。

select  d.Name as Department, a.Name as Employee, a.Salary as Salary
from (
select *, dense_rank() over (partition by DepartmentId
order by Salary desc) as salary_rank
from Employee
) as a, Department as d
where salary_rank <= 3 and a.DepartmentId = d.Id;

总结

TOPN 问题,一般分为单表不分组、多表不分组、单表分组、多表分组。单表不分组一般用 limit 子句就能解决,多表不分组一般用 limit 子句加上联结就能解决。单表分组、多表分组一般用窗口函数和联结解决。

涉及到“既要分组,又要排序”的情况,要能想到用窗口函数来实现。

196. 删除重复的电子邮箱

题目描述

编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。

+----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+------------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下几行: +----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
+----+------------------+ 提示: 执行 SQL 之后,输出是整个 Person 表。
使用 delete 语句。
# 表架构

解法

分析


总结

197. 上升的温度

题目描述


# 表架构

解法

分析

总结

262. 行程和用户

题目描述


# 表架构

解法

分析

总结

595. 大的国家

题目描述


# 表架构

解法

分析

总结

596. 超过5名学生的课

题目描述


# 表架构

解法

分析

总结

601. 体育馆的人流量

题目描述


# 表架构

解法

分析

总结

620. 有趣的电影

题目描述


# 表架构

解法

分析

总结

626. 换座位

题目描述


# 表架构

解法

分析

总结

627. 交换工资

题目描述


# 表架构

解法

分析

总结

【MySQL 基础】MySQ LeetCode的更多相关文章

  1. MYSQ系列-MYSQL基础增强(Mysql基本语句)

    MYSQL基础增强 库操作 创建一个使用UTF-8字符集的数据库: create database mydb character set UTF8; 创建一个带校对集的数据库 create datab ...

  2. MySQL基础操作命令

    MySQL基础操作命令 1. 查看MySQL进程 ps -ef|grep mysql |grep -v grep 2. 查看MySQL端口 ss -lnt | grep 3306 3. MySQL的启 ...

  3. Mysql基础代码(不断完善中)

    Mysql基础代码,不断完善中~ /* 启动MySQL */ net start mysql /* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权限 ...

  4. MYSQL基础操作

    MYSQL基础操作 [TOC] 1.基本定义 1.1.关系型数据库系统 关系型数据库系统是建立在关系模型上的数据库系统 什么是关系模型呢? 1.数据结构可以规定,同类数据结构一致,就是一个二维的表格 ...

  5. 【夯实Mysql基础】记一次mysql语句的优化过程

    1. [事件起因] 今天在做项目的时候,发现提供给客户端的接口时间很慢,达到了2秒多,我第一时间,抓了接口,看了运行的sql,发现就是 2个sql慢,分别占了1秒多. 一个sql是 链接了5个表同时使 ...

  6. MySQL基础(非常全)

    MySQL基础 一.MySQL概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access ...

  7. mysql 基础篇5(mysql语法---数据)

    6 增删改数据 -- ********一.增删改数据********* --- -- 1.1 增加数据 -- 插入所有字段.一定依次按顺序插入 INSERT INTO student VALUES(1 ...

  8. MySQL 基础语句

    MySQL 基础语句 多个知识点 ----------------------------------------------------------------------------------- ...

  9. MySQL:基础—数据分组

    MySQL:基础-数据分组 1.为什么要分组: 比如一个表中有多条订单记录,如上图,每条记录对应着一个商品,现在我要查询 每个商品被订购的单数 准备出货?也就是找到每个商品被订购的数量. 如果只找一个 ...

  10. MySQL基础学习总结

    1.MySQL基础概念 mysql逻辑架构如下: 每个客户端连接都会在服务器中拥有一个线程,这个连接的查询只会在这个单独的线程中执行. MySQL是分层的架构.上层是服务器层的服务和查询执行引擎,下层 ...

随机推荐

  1. vue基础题

    一.对于MVVM的理解? MVVM 是 Model-View-ViewModel 的缩写. Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑. View 代表UI 组件,它负责 ...

  2. Panda Global获悉,美国承诺4年内明确区块链数字资产监管方式!

    近日,美国商品期货交易委员会(CFTC)宣布,在4年内将会全面把加密货币监管列为优先事项.Panda Global从7月8日公布的新战略中获悉,此次CFTC公布了自己接下来的新框架,并且在框架中承诺: ...

  3. AT2688 [ARC080C] Young Maids

    一道挺有意思的题目,在这里记录一下. 题目大意 给你一个长度为 \(n\) 的排列,每一次你可以取出相邻的两个数将其放在答案序列的开头,最后问你字典序最小的答案序列是什么. 题解 由于最后是求字典序最 ...

  4. HDU3306 Another kind of Fibonacci

    本篇题解用于作者本人对于矩阵乘法的印象加深,也欢迎大家的阅读. 题目大意 众所周知,斐波那契数列为 \(f(0)=1\) , \(f(1)=1\) ,\(f(n)=f(n-1)+f(n-2)~(n&g ...

  5. 第一章、Docker 简介

    笔记内容来自:第一本Docker书 [澳] James Turnbull 著 李兆海 刘斌 巨震 ​ Docker 是一个能够把开发的应用程序自动部署到容器的开源引擎.(由Docker 公司,前dot ...

  6. uni-app微信小程序登录授权

    微信小程序授权是非常简单和常用的功能,但为了方便,还是在此记录一下要点: 首先是需要用到一个授权按钮来触发获取用户信息授权: 关键在于 open-type 为 getUserInfo , 然后有个@g ...

  7. GaussDB(DWS)磁盘维护:vacuum full执行慢怎么办?

    摘要:在数据库中用于维护数据库磁盘空间的工具是VACUUM,其重要的作用是删除那些已经标示为删除的数据并释放空间. vacuum的功能 回收空间 数据库总是不断地在执行删除,更新等操作.良好的空间管理 ...

  8. Javascript之Firefox与IE

    IE其实相对来讲并不是规范的遵循者,错怪firefox了. 2020注:IE看来要退出市场了,这些也逐渐成为历史了.:) 1firefox不支持iframe.document, 而IE支持,所以对fi ...

  9. Git - 简单的使用与Github

    Github: Following the instructions to create repo. Git on Linux(centos): download the latest GIT and ...

  10. Python利用zmail收取邮件

    收取邮件一般用pop和imap,这里使用国人大神开发的zmail来收取: 1 ''' 2 #利用zmail收取邮件 3 #只要几行代码 4 #安装库:pip3 install zmail 5 #国内大 ...