高级字符串函数 / 正则表达式 / 子句
针对特定文本格式的处理,SQL 提供了强大的字符串函数和正则 (REGEXP) 支持。
1667. 修复表中的名字 (Fix Names in a Table)
题目描述
Users 表包含用户的 ID 和姓名:
user_id: 用户 ID,主键。name: 用户姓名。
请编写一个解决方案,修复 name 字段,使其符合以下格式:
- 第一个字母大写。
- 其余字母均为小写。
结果按
user_id排序。
解题思路
字符串拆分与重组。
- 切片:使用
SUBSTRING(name, 1, 1)提取首字母,使用SUBSTRING(name, 2)提取剩余部分。 - 转换:对首字母应用
UPPER,对剩余部分应用LOWER。 - 拼接:使用
CONCAT将两部分合并。
SQL 实现 (MySQL)
SELECT user_id,
CONCAT(UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2))) AS name
FROM Users
ORDER BY user_id;
1527. 患某种疾病的患者 (Patients With a Condition)
题目描述
Patients 表包含患者的 ID、姓名以及患有的疾病代码:
patient_id: 患者 ID。patient_name: 患者姓名。conditions: 疾病代码列表(由空格分隔)。
请编写一个解决方案,找出所有患有 I 类糖尿病 (Type I Diabetes) 的患者。该疾病的代码以 DIAB1 开头。
解题思路
正则单词边界匹配。
- 多情况匹配:目标代码
DIAB1可能出现在字符串的最开头,也可能出现在中间(前面有一个空格)。 - 规则:
^DIAB1匹配开头,DIAB1匹配后续单词开头。 - 实现:使用
REGEXP或者LIKE操作。
SQL 实现 (MySQL)
SELECT patient_id, patient_name, conditions
FROM Patients
WHERE conditions REGEXP '^DIAB1| DIAB1';
196. 删除重复的电子邮箱 (Delete Duplicate Emails)
题目描述
Person 表包含员工的 ID 和电子邮箱。
请编写一个解决方案,删除 所有重复的电子邮箱,只保留 id 最小的那个。注意,这是一个删除操作,你应该直接修改 Person 表。
解题思路
自连接删除逻辑。
- 目标定位:找出那些满足
p1.email = p2.email且p1.id > p2.id的记录。这些记录就是需要被清理的重复项。 - 操作:使用
DELETE p1 FROM Person p1 JOIN Person p2 ...语法实现原地删除。
SQL 实现 (MySQL)
DELETE p1
FROM Person p1 JOIN Person p2 ON p1.email = p2.email
WHERE p1.id > p2.id;
176. 第二高的薪水 (Second Highest Salary)
题目描述
Employee 表记录了员工的薪水信息。
请编写一个解决方案,查询 第二高 的薪水。如果不存在第二高的薪水(例如表中只有一条记录或所有薪水都相同),则结果应返回 null。
解题思路
去重排序与空表处理。
- 获取位次:使用
DISTINCT去重并ORDER BY desc排序,然后用LIMIT 1 OFFSET 1获取第二名。 - 返回 NULL:简单的
LIMIT查询在没找到数据时会返回一个空结果集(Empty set),而不是NULL。为了在没有数据时返回NULL,必须将其包装为子查询。
SQL 实现 (MySQL)
SELECT (
SELECT DISTINCT salary
FROM Employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1
) AS SecondHighestSalary;
1484. 按日期分组销售产品 (Group Sold Products By The Date)
题目描述
Activities 表记录了每天销售的产品名称。
请编写一个解决方案,按 日期 (sell_date) 分组,统计:
- 销售的不同产品数量 (
num_sold)。 - 这些产品的名称列表 (
products),按字符顺序升序排列并以逗号分隔。 结果按sell_date排序。
解题思路
组内连接函数 (GROUP_CONCAT)。
- 去重统计:使用
COUNT(DISTINCT product)计算种类数。 - 字符合并:使用
GROUP_CONCAT(DISTINCT product ORDER BY product SEPARATOR ',')将组内的产品名称按照要求进行去重、排序及拼接。
SQL 实现 (MySQL)
SELECT sell_date,
COUNT(DISTINCT product) AS num_sold,
GROUP_CONCAT(DISTINCT product ORDER BY product SEPARATOR ',') AS products
FROM Activities
GROUP BY sell_date
ORDER BY sell_date;
1327. 列出指定时间段内所有的下单产品 (List the Products Ordered in a Period)
题目描述
你有两张表:
Products: 包含product_id,product_name,product_category。Orders: 包含product_id,order_date,unit。
请编写一个解决方案,找出所有在 2020 年 2 月 累计下单数 大于等于 100 的产品名称及其对应的累计下单数量。
解题思路
过滤关联与分组过滤。
- 连接:通过
product_id关联两表。 - 时间过滤:筛选
order_date在 2020 年 2 月的记录。 - 聚合筛选:按产品 ID 分组,计算
SUM(unit),并使用HAVING子句过滤出总量不小于 100 的行。
SQL 实现 (MySQL)
SELECT p.product_name, SUM(o.unit) AS unit
FROM Products p JOIN Orders o USING(product_id)
WHERE o.order_date LIKE '2020-02-%'
GROUP BY p.product_id
HAVING unit >= 100;
1517. 查找拥有有效邮箱的用户 (Find Users With Valid E-Mails)
题目描述
Users 表记录了用户的信息。
user_id: 用户 ID,主键。name: 用户姓名。mail: 用户的邮箱地址。
请编写一个解决方案,找出所有拥有 有效邮箱 的用户。 有效邮箱的规则如下:
- 包含名称和域名,域名必须是
@leetcode.com。 - 名称 必须以字母开头。
- 名称 可以包含字母(大写或小写)、数字、下划线
_、点号.或连字号-。
解题思路
正则表达式匹配 (REGEXP)。
- 开头锚定:使用
^确保从字符串最前端开始。 - 首位字符:使用
[A-Za-z]匹配字母开头。 - 合法后续:使用
[A-Za-z0-9_.-]*表达剩余部分的允许范围。注意-在方括号内由于其特殊性通常放在最后。 - 域名固定:匹配
@leetcode[.]com。注意点号.在正则中是特殊字符,需使用[.]或转义。 - 末尾锚定:使用
$确保域名后没有多余内容。
SQL 实现 (MySQL)
SELECT user_id, name, mail
FROM Users
WHERE mail REGEXP '^[A-Za-z][A-Za-z0-9_.-]*@leetcode[.]com$';
(注:MySQL 的正则表达式执行时取决于具体版本和变量配置,可能对大小写不敏感)。