Update SQL 练习.md

补充交换工资的 CASE 解法与分数排名的窗口函数解法;调整大国家、删除重复邮箱等题目的 SQL 语句与原理解析,并完成多处文字校对。
This commit is contained in:
CyC2018
2026-10-30 20:18:51 +08:00
parent bd65d9ae09
commit bf7ffe3f31
+43 -20
View File
@@ -38,7 +38,7 @@ https://leetcode.com/problems/big-countries/description/
+-----------------+------------+------------+--------------+---------------+
```
查找面积超过 3,000,000 或者人口数超过 25,000,000 的国家。
查找面积至少 3,000,000 或者人口至少 25,000,000 的国家。
```text
+--------------+-------------+--------------+
@@ -58,8 +58,8 @@ SELECT name,
FROM
World
WHERE
area > 3000000
OR population > 25000000;
area >= 3000000
OR population >= 25000000;
```
### SQL Schema
@@ -110,7 +110,7 @@ https://leetcode.com/problems/swap-salary/description/
两个相等的数异或的结果为 0,而 0 与任何一个数异或的结果为这个数。
sex 字段只有两个取值:'f' 和 'm',并且有以下规律:
sex 字段只有两个取值:'f' 和 'm',并且有以下规律:
```text
'f' ^ ('m' ^ 'f') = 'm' ^ ('f' ^ 'f') = 'm'
@@ -124,6 +124,13 @@ UPDATE salary
SET sex = CHAR ( ASCII(sex) ^ ASCII( 'm' ) ^ ASCII( 'f' ) );
```
用 CASE 写更直观:
```sql
UPDATE salary
SET sex = CASE sex WHEN 'm' THEN 'f' ELSE 'm' END;
```
### SQL Schema
```sql
@@ -189,7 +196,7 @@ ORDER BY
DROP TABLE
IF
EXISTS cinema;
CREATE TABLE cinema ( id INT, movie VARCHAR ( 255 ), description VARCHAR ( 255 ), rating FLOAT ( 2, 1 ) );
CREATE TABLE cinema ( id INT, movie VARCHAR ( 255 ), description VARCHAR ( 255 ), rating DECIMAL ( 2, 1 ) );
INSERT INTO cinema ( id, movie, description, rating )
VALUES
( 1, 'War', 'great 3D', 8.9 ),
@@ -233,7 +240,7 @@ https://leetcode.com/problems/classes-more-than-5-students/description/
### Solution
对 class 列进行分组之后,再使用 count 汇总函数统计每个分组的记录个数,之后使用 HAVING 进行筛选。HAVING 针对分组进行筛选,而 WHERE 针对每个记录(行)进行筛选。
对 class 列进行分组之后,再使用 count 汇总函数统计每个分组的记录个数,之后使用 HAVING 进行筛选。HAVING 针对分组进行筛选,而 WHERE 针对每个记录(行)进行筛选。
```sql
SELECT
@@ -296,7 +303,7 @@ https://leetcode.com/problems/duplicate-emails/description/
### Solution
对 Email 进行分组,如果并使用 COUNT 进行计数统计,结果大于等于 2 的表示 Email 重复。
对 Email 进行分组,并使用 COUNT 进行计数统计,结果大于等于 2 的表示 Email 重复。
```sql
SELECT
@@ -366,7 +373,7 @@ FROM
Person p2
WHERE
p1.Email = p2.Email
AND p1.Id > p2.Id
AND p1.Id > p2.Id;
```
子查询:
@@ -386,7 +393,7 @@ WHERE
);
```
应该注意的是上述解法额外嵌套了一个 SELECT 语句,如果不这么做,会出现错误:You can't specify target table 'Person' for update in FROM clause。以下演示了这种错误解法。
需要注意的是,上述解法额外嵌套了一个 SELECT 语句,如果不这么做,会出现错误:You can't specify target table 'Person' for update in FROM clause。这是因为 MySQL 不允许在修改一张表的同时在子查询中读取它;嵌套后内层结果先物化成临时表,外层读的是临时表。以下演示了这种错误解法。
```sql
DELETE
@@ -400,7 +407,7 @@ WHERE
);
```
参考:[pMySQL Error 1093 - Can't specify target table for update in FROM clause](https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause)
参考:[MySQL Error 1093 - Can't specify target table for update in FROM clause](https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause)
### SQL Schema
@@ -567,7 +574,7 @@ Orders 表:
### Solution
左外链接
左外连接
```sql
SELECT
@@ -594,6 +601,8 @@ WHERE
);
```
需要注意的是,子查询的结果中有 NULL 时,NOT IN 不返回任何行,这时改用 NOT EXISTS 或者左外连接。
### SQL Schema
```sql
@@ -731,7 +740,7 @@ https://leetcode.com/problems/second-highest-salary/description/
### Solution
为了在没有查找到数据时返回 null,需要在查询结果外面再套一层 SELECT。
为了在没有查找到数据时返回 null,需要在查询结果外面再套一层 SELECT:作为标量子查询,没有结果时它的值为 NULL。
```sql
SELECT
@@ -763,6 +772,8 @@ VALUES
### Solution
LIMIT 的参数不能是 N - 1 这样的表达式,因此先把 N 减 1。
```sql
CREATE FUNCTION getNthHighestSalary ( N INT ) RETURNS INT READS SQL DATA BEGIN
@@ -852,7 +863,7 @@ ORDER BY
|1| 4.1 |2| 4.2|
|1| 4.1 |3| 4.3|
可以看到每个 S1.score 都有对应好几条记录,再进行分组,并统计每个分组的数量作为 'Rank'
可以看到每个 S1.score 都有对应好几条记录,再进行分组,并统计每个分组的数量作为 'Rank'。
```sql
SELECT
@@ -903,12 +914,12 @@ ORDER BY
| 2 | 4.2 | 3 | 4.2 |
| 2 | 4.2 | 2 | 4.2 |
| 3 | 4.2 | 3 | 4.2 |
| 3 | 4.2 | 2 | 4.1 |
| 3 | 4.2 | 2 | 4.2 |
| 1 | 4.1 | 3 | 4.2 |
| 1 | 4.1 | 2 | 4.2 |
| 1 | 4.1 | 1 | 4.1 |
想要的结果是,把分数相同的放在同一个排名,并且相同分数只占一个位置,例如上面的分数,Id=2 和 Id=3 的记录都有相同的分数,并且最高,他们并列第一。而 Id=1 的记录应该排第二名,而不是第三名。所以在进行 COUNT 计数统计时,需要使用 COUNT( DISTINCT S2.score ) 从而只统计一次相同的分数。
想要的结果是,把分数相同的放在同一个排名,并且相同分数只占一个位置,例如上面的分数,Id=2 和 Id=3 的记录都有相同的分数,并且最高,它们并列第一。而 Id=1 的记录应该排第二名,而不是第三名。所以在进行 COUNT 计数统计时,需要使用 COUNT( DISTINCT S2.score ) 从而只统计一次相同的分数。
```sql
SELECT
@@ -924,6 +935,18 @@ ORDER BY
S1.score DESC;
```
MySQL 8.0 起也可以直接用窗口函数 DENSE_RANK(),它给相同的分数相同的排名,并且排名不跳号:
```sql
SELECT
score 'Score',
DENSE_RANK() OVER ( ORDER BY score DESC ) 'Rank'
FROM
Scores
ORDER BY
score DESC;
```
### SQL Schema
```sql
@@ -982,10 +1005,10 @@ FROM
Logs L1,
Logs L2,
Logs L3
WHERE L1.id = l2.id - 1
WHERE L1.id = L2.id - 1
AND L2.id = L3.id - 1
AND L1.num = L2.num
AND l2.num = l3.num;
AND L2.num = L3.num;
```
### SQL Schema
@@ -993,9 +1016,9 @@ WHERE L1.id = l2.id - 1
```sql
DROP TABLE
IF
EXISTS LOGS;
CREATE TABLE LOGS ( Id INT, Num INT );
INSERT INTO LOGS ( Id, Num )
EXISTS Logs;
CREATE TABLE Logs ( Id INT, Num INT );
INSERT INTO Logs ( Id, Num )
VALUES
( 1, 1 ),
( 2, 1 ),