跳到主要内容

高级字符串函数 / 正则表达式 / 子句

针对特定文本格式的处理,SQL 提供了强大的字符串函数和正则 (REGEXP) 支持。

1667. 修复表中的名字 (Fix Names in a Table)

题目描述

Users 表包含用户的 ID 和姓名:

  • user_id: 用户 ID,主键。
  • name: 用户姓名。

请编写一个解决方案,修复 name 字段,使其符合以下格式:

  • 第一个字母大写。
  • 其余字母均为小写。 结果按 user_id 排序。

解题思路

字符串拆分与重组

  1. 切片:使用 SUBSTRING(name, 1, 1) 提取首字母,使用 SUBSTRING(name, 2) 提取剩余部分。
  2. 转换:对首字母应用 UPPER,对剩余部分应用 LOWER
  3. 拼接:使用 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 开头。

解题思路

正则单词边界匹配

  1. 多情况匹配:目标代码 DIAB1 可能出现在字符串的最开头,也可能出现在中间(前面有一个空格)。
  2. 规则^DIAB1 匹配开头, DIAB1 匹配后续单词开头。
  3. 实现:使用 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 表。

解题思路

自连接删除逻辑

  1. 目标定位:找出那些满足 p1.email = p2.emailp1.id > p2.id 的记录。这些记录就是需要被清理的重复项。
  2. 操作:使用 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

解题思路

去重排序与空表处理

  1. 获取位次:使用 DISTINCT 去重并 ORDER BY desc 排序,然后用 LIMIT 1 OFFSET 1 获取第二名。
  2. 返回 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)

  1. 去重统计:使用 COUNT(DISTINCT product) 计算种类数。
  2. 字符合并:使用 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 的产品名称及其对应的累计下单数量。

解题思路

过滤关联与分组过滤

  1. 连接:通过 product_id 关联两表。
  2. 时间过滤:筛选 order_date 在 2020 年 2 月的记录。
  3. 聚合筛选:按产品 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: 用户的邮箱地址。

请编写一个解决方案,找出所有拥有 有效邮箱 的用户。 有效邮箱的规则如下:

  1. 包含名称和域名,域名必须是 @leetcode.com
  2. 名称 必须以字母开头。
  3. 名称 可以包含字母(大写或小写)、数字、下划线 _、点号 . 或连字号 -

解题思路

正则表达式匹配 (REGEXP)

  1. 开头锚定:使用 ^ 确保从字符串最前端开始。
  2. 首位字符:使用 [A-Za-z] 匹配字母开头。
  3. 合法后续:使用 [A-Za-z0-9_.-]* 表达剩余部分的允许范围。注意 - 在方括号内由于其特殊性通常放在最后。
  4. 域名固定:匹配 @leetcode[.]com。注意点号 . 在正则中是特殊字符,需使用 [.] 或转义。
  5. 末尾锚定:使用 $ 确保域名后没有多余内容。

SQL 实现 (MySQL)

SELECT user_id, name, mail 
FROM Users
WHERE mail REGEXP '^[A-Za-z][A-Za-z0-9_.-]*@leetcode[.]com$';

(注:MySQL 的正则表达式执行时取决于具体版本和变量配置,可能对大小写不敏感)