sql练习题单-知识点总结
一、题单
力扣
题目分类 | 经典题目 | 核心考点 | 难度 |
---|---|---|---|
基础查询与过滤 | 1757. 可回收且低脂的产品 595. 大的国家 1148. 文章浏览 I | WHERE 条件过滤、OR 逻辑运算符 | 简单 |
表连接(JOIN) | 1378. 使用唯一标识码替换员工ID 1068. 产品销售分析 I 577. 员工奖金 1581. 进店却未进行过交易的顾客 1251. 平均售价 | LEFT JOIN 、INNER JOIN 、多表连接、连接条件与过滤 | 简单-中等 |
排序、分组与聚合 | 182. 查找重复的电子邮箱 1484. 按日期分组销售产品 619. 只出现一次的最大数字 1174. 即时食物配送 II | GROUP BY 、HAVING 、COUNT(DISTINCT) 、GROUP_CONCAT | 简单-中等 |
子查询与高级功能 | 176. 第二高的薪水 570. 至少有5名直接下属的经理 185. 部门工资前三高的所有员工 610. 判断三角形 | 子查询、CASE WHEN 、IFNULL /COALESCE 、逻辑判断 | 中等 |
窗口函数 | 178. 分数排名 | RANK() 、DENSE_RANK() 、ROW_NUMBER() |
题目详解:
182
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 ASC;
部分 | 说明 |
---|---|
GROUP_CONCAT(...) | MySQL 特有函数,把一组中的多个值拼接成一个字符串 |
DISTINCT product | 只取不同的产品名(去重) |
ORDER BY product | 拼接时按产品名的字母顺序排列 |
SEPARATOR ',' | 用逗号 , 作为分隔符(默认就是逗号,可省略) |
AS products | 给拼接结果起别名 |
函数/语法 | 用途 | 注意 |
---|---|---|
GROUP BY | 按某字段分组 | 是聚合操作的基础 |
COUNT(DISTINCT col) | 统计去重后的数量 | 非常常用 |
GROUP_CONCAT() | 将多行值拼成字符串 | MySQL 特有 |
ORDER BY in GROUP_CONCAT | 控制拼接顺序 | 否则顺序不确定 |
SEPARATOR ',' | 自定义分隔符 | 可改为 ; 或 ` |
这种写法常用于:
- 日报:每天卖了哪些商品?
- 用户行为分析:每个用户购买了哪些类目?
- 标签系统:每个人有哪些标签?
✅ 总结一句话:
这段 SQL 的核心思想是:
“按日期分组 → 去重统计 → 拼接字符串”
178
SELECTS.score,DENSE_RANK() OVER (ORDER BYS.score DESC) AS 'rank'
FROMScores S;
这是最核心的部分,我们拆开讲:
部分 | 说明 |
---|---|
DENSE_RANK() | 一个窗口函数(Window Function),用于生成密集排名 |
OVER (...) | 定义窗口函数的作用范围和排序规则 |
ORDER BY S.score DESC | 按分数 从高到低 排序(DESC = 降序) |
AS 'rank' | 将排名结果命名为 'rank' (注意:rank 是保留字,所以用引号) |
176
# Write your MySQL query statement below
SELECTIFNULL((SELECT DISTINCT SalaryFROM EmployeeORDER BY Salary DESCLIMIT 1 OFFSET 1),NULL) AS SecondHighestSalary
1. 内层子查询:找出第二高的薪水
步骤 | 说明 |
---|---|
DISTINCT Salary | 去重:相同薪水只保留一个(避免重复影响排名) |
ORDER BY Salary DESC | 按薪水从高到低排序(最高在前) |
LIMIT 1 OFFSET 1 | 跳过第 1 行,取 1 行 - OFFSET 1 :跳过最高薪- LIMIT 1 :取接下来的 1 条(即第二高) |
. 外层:IFNULL(..., NULL)
sql
编辑
IFNULL( 子查询结果 , NULL )
IFNULL(value, default)
:如果value
是NULL
,就返回default
- 这里
default
也是NULL
,看起来“多此一举”,其实非常关键!
🤔 为什么需要 IFNULL
?
考虑一种情况:
- 表里只有 1 个员工,或者所有人薪水都一样
- 那么子查询
LIMIT 1 OFFSET 1
会查不到任何数据 - 查不到数据 → 返回
NULL
- 但
SELECT NULL
仍然会返回一行NULL
,而不是“空结果
核心考点与刷题技巧
根据搜索结果显示,以下这些是面试中频繁出现的核心考点,你需要特别注意:
NULL值处理:这是面试官的挚爱考点。 搜索结果显示,在过滤条件中,
NULL
不能直接用=
或!=
比较,必须使用IS NULL
或IS NOT NULL
。例如“寻找用户推荐人”一题,referee_id != 2
不会包含referee_id
为NULL
的记录。表连接与连接条件:务必理解清楚不同JOIN的区别。
多表连接:如“学生们参加各科测试的次数”一题,需要先通过
CROSS JOIN
生成所有学生和所有科目的组合,再LEFT JOIN
考试成绩,才能确保不漏掉任何学生-科目组合。连接条件与过滤:
ON
和WHERE
的执行顺序不同,会影响结果。例如“平均售价”一题,需要将purchase_date BETWEEN start_date AND end_date
这个条件放在ON
子句中,而不是WHERE
里,否则会错误地过滤掉没有销售记录的产品。
聚合函数与CASE WHEN:
CASE WHEN
与AVG
等聚合函数结合,可以巧妙地计算比率。在“确认率”一题中,使用AVG(CASE WHEN action = 'confirmed' THEN 1 ELSE 0 END)
可以简洁地得出确认率,并自动处理NULL
值。窗口函数:这是解决复杂问题的利器,常用于排名、累计求和等问题。你需要掌握
ROW_NUMBER()
,RANK()
,DENSE_RANK()
的区别,以及SUM() OVER()
的用法。
我的解题思路4步
1.查什么
直接从题意或者输出,知道SELECT什么,有的内容需要处理列入count(xx)as x,或者再内嵌一个函数窗口函数什么的。
2.确表源,
from,以及表之间的关系,jion
from xxx left join ccc
on xxx.id=ccc.id
3.找条件,
普通查询
where 条件
order by xxx ESC(升序)/DESC(降序)
LIMIT 数量
分组统计
where
group by xxx
having 过滤条件。
4.完成构建检查。
高频考点速记:
• 分组统计 → GROUP BY + HAVING
• 多表关联 → JOIN ON
• 排名问题 → ROW_NUMBER/RANK
• 连续问题 → LAG/LEAD + 日期计算
• 空值处理 → COALESCE/IFNULL
基础查询模板
1. 简单查询框架
sql
SELECT 字段 FROM 表 WHERE 条件 ORDER BY 排序字段 LIMIT 数量;
2. 分组统计模板
sql
SELECT 分组字段, 聚合函数(统计字段) FROM 表 WHERE 过滤条件 GROUP BY 分组字段 HAVING 分组后过滤条件;
3. 内连接模板
sql
SELECT A.字段, B.字段 FROM 表A A INNER JOIN 表B B ON A.关联字段 = B.关联字段;
4. 左连接模板
sql
SELECT A.字段, B.字段 FROM 表A A LEFT JOIN 表B B ON A.关联字段 = B.关联字段;
5. 排名前N模板
sql
SELECT 字段 FROM (SELECT 字段,ROW_NUMBER() OVER (ORDER BY 排序字段 DESC) as rnFROM 表 ) t WHERE rn <= N;
6. 部门排名模板
sql
SELECT 字段 FROM (SELECT 字段,RANK() OVER (PARTITION BY 部门字段 ORDER BY 薪资字段 DESC) as rankFROM 表 ) t WHERE rank = 1;
7. 累计统计模板
sql
SELECT 日期字段, 数值字段,SUM(数值字段) OVER (ORDER BY 日期字段) as 累计值 FROM 表;
8. 同比环比模板
sql
SELECT 日期, 数值,LAG(数值, 1) OVER (ORDER BY 日期) as 上月数值,(数值 - LAG(数值, 1) OVER (ORDER BY 日期)) / LAG(数值, 1) OVER (ORDER BY 日期) as 增长率 FROM 表;
9. 连续登录模板
sql
SELECT user_id, COUNT(*) as 连续天数 FROM (SELECT user_id, login_date,DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) as flagFROM login_records ) t GROUP BY user_id, flag HAVING COUNT(*) >= N;
10. 空值替换模板
sql
SELECT COALESCE(字段, 默认值) as 新字段名 FROM 表;