吃透这些SQL面试考点,80%的面试题都能拿捏!
很多小伙伴面试数据开发、数据分析、后端岗位时,SQL都是必考核心项。但大部分人刷题杂乱,面试时遇到变形题就卡壳。
今天我把全网高频的SQL面试考点,从基础必背、核心查询、聚合分组、窗口函数、多表关联、优化实战、面试坑点七个维度系统总结,全程干货无废话,适合收藏复盘、考前突击!
一、基础概念考点(面试开篇必问)
这部分属于送分题,面试官主要考察基本功是否扎实,杜绝基础漏洞。
1. SQL分类(高频问答)
- DDL(数据定义语言):定义数据库、表结构,无事务、不可回滚。
核心语句:CREATE、ALTER、DROP、TRUNCATE - DML(数据操作语言):操作表中数据,支持事务、可回滚。
核心语句:SELECT、INSERT、UPDATE、DELETE - DCL(数据控制语言):权限管理。
核心语句:GRANT、REVOKE - TCL(事务控制语言):事务管理。
核心语句:COMMIT、ROLLBACK、SAVEPOINT
2. 关键易混概念
DELETE VS TRUNCATE VS DROP(面试超高频对比题)
- DELETE:删除表数据,保留表结构,支持事务回滚,速度慢,可加WHERE条件精准删除
- TRUNCATE:清空全表数据,保留表结构,不支持事务,速度极快,无法精准删除单行
- DROP:删除整张表(数据+结构),释放存储空间,不可恢复,彻底删除
3. 事务四大特性ACID
所有后端、数据岗位必背:原子性、一致性、隔离性、持久性
- 原子性:事务要么全部执行成功,要么全部失败回滚,不可拆分
- 一致性:事务执行前后,数据库数据完整性不变(约束、索引有效)
- 隔离性:多个事务并发执行,互不干扰
- 持久性:事务提交后,数据永久落地,断电不丢失
4. 事务隔离级别与并发问题
面试常问:脏读、不可重复读、幻读的区别,以及隔离级别解决的问题
- 读未提交:存在脏读、不可重复读、幻读
- 读已提交(MySQL默认):解决脏读,存在不可重复读、幻读
- 可重复读:解决脏读、不可重复读,存在幻读
- 串行化:解决所有并发问题,性能最低,完全串行执行
二、核心查询语法(笔试必考)
重点掌握SQL执行顺序,90%的语法错误都是因为搞反了执行逻辑!
1. SQL完整执行顺序(重中之重)
手写顺序:SELECT → FROM → JOIN → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
真实执行顺序:
FROM/JOIN(确定数据源)→ WHERE(过滤行数据)→ GROUP BY(分组)→ 聚合函数计算 → HAVING(过滤分组后数据)→ SELECT(选取字段)→ ORDER BY(排序)→ LIMIT(分页)
💡 面试高频坑:WHERE不能过滤聚合结果,HAVING专门过滤分组聚合后的数据
2. 条件查询核心要点
- NULL判断:只能用 IS NULL / IS NOT NULL,不能用 = NULL(NULL不等于任何值)
- 模糊查询:LIKE %关键词%(全模糊)、关键词%(右索引有效)、%关键词(左索引失效)
- 范围查询:IN、BETWEEN AND、EXISTS(高效)、NOT EXISTS
3. 例题精讲
例题一(入门):条件筛选与NULL判断
题目:现有员工表 emp(id,name,salary,entry_time,dept_id),查询出薪资不为空、部门编号为10或20的员工姓名和薪资。
错误写法:
sql
SELECT name,salary
FROM emp
WHERE salary != NULL
AND dept_id IN(10,20);
错误原因:NULL不能用!=、= 判断,数据库中 NULL 不等于任何值(包括自身),所有普通运算符与NULL运算结果都为NULL,查询不到任何数据。
正确写法:
sql
SELECT name,salary
FROM emp
WHERE salary IS NOT NULL
AND dept_id IN(10,20);
核心考点:NULL专属判断语法 IS NULL / IS NOT NULL,属于面试必背基础细节。
例题二(基础):模糊查询与索引坑点
题目:在员工表emp中,查询姓名以"张"开头、姓名包含"伟"、姓名最后一个字是"明"的员工数据,分别写出对应SQL,并说明索引有效性。
标准答案:
sql
-- 1、姓名以张开头(索引有效)
SELECT * FROM emp WHERE name LIKE '张%';
-- 2、姓名包含伟(全模糊,索引失效)
SELECT * FROM emp WHERE name LIKE '%伟%';
-- 3、姓名以明结尾(左模糊,索引失效)
SELECT * FROM emp WHERE name LIKE '%明';
核心考点:通配符%放前面,索引失效 ;%放后面,保留索引。业务中尽量避免左模糊、全模糊查询,大数据量场景会导致慢SQL。
例题三(进阶易错):WHERE与HAVING区分+执行顺序
题目:员工表emp(dept_id 部门号,salary 薪资),统计各部门平均薪资,只筛选出平均薪资大于8000的部门,写出正确SQL。
错误写法:
sql
SELECT dept_id,AVG(salary) AS avg_salary
FROM emp
WHERE avg_salary > 8000
GROUP BY dept_id;
错误原因:根据SQL执行顺序,WHERE在聚合函数之前执行,此时还未计算出平均薪资,WHERE无法过滤聚合结果,直接报错。分组后的聚合筛选,必须用HAVING。
正确写法:
sql
SELECT dept_id,AVG(salary) AS avg_salary
FROM emp
GROUP BY dept_id
HAVING AVG(salary) > 8000;
解题逻辑:先WHERE过滤原始数据(行过滤)→ 再GROUP BY分组 → 最后HAVING过滤分组后的聚合数据(组过滤)。
例题四(拔高):EXISTS与IN性能场景题
题目:现有大表员工表emp(100w数据)、小表部门表dept(几十条数据),查询存在对应部门信息的员工数据,分别用IN和EXISTS实现,并说明性能优劣。
写法1:IN查询
sql
SELECT * FROM emp
WHERE dept_id IN (SELECT dept_id FROM dept);
写法2:EXISTS查询(大表场景优先)
sql
SELECT * FROM emp e
WHERE EXISTS (SELECT 1 FROM dept d WHERE e.dept_id = d.dept_id);
核心考点总结:
- 外层大表、内层小表:EXISTS性能更高,遍历大表、匹配小表,循环次数少
- 外层小表、内层大表:IN性能更高
- 面试通用结论:大表查询优先用EXISTS,替代IN
三、聚合与分组(数据分析核心)
分组统计是面试、笔试、实操的高频场景,核心是理解分组规则。
1. 常用聚合函数
SUM、COUNT、AVG、MAX、MIN
高频考点:COUNT(*)、COUNT(字段)、COUNT(1)的区别
- COUNT(*):统计所有行数,包含NULL行、重复行,性能最优
- COUNT(字段):统计字段非NULL的行数,会忽略NULL数据
- COUNT(1):效果和COUNT(*)基本一致,无本质区别
2. GROUP BY 分组规则
核心原则:SELECT 后的非聚合字段,必须出现在 GROUP BY 后面(MySQL宽松模式除外,正规面试必须遵守)
经典场景:统计每个部门的员工人数、平均薪资、最高薪资
易错点:先WHERE过滤原始数据,再GROUP BY分组,最后HAVING过滤分组结果
3. 例题精讲
例题一(入门):基础聚合统计(无分组)
题目:基于员工表 emp,统计全公司员工总人数、最高薪资、最低薪资、平均薪资、薪资总和。
正确写法:
sql
SELECT
COUNT(*) AS total_staff, -- 总员工数
MAX(salary) AS max_salary, -- 最高薪资
MIN(salary) AS min_salary, -- 最低薪资
AVG(salary) AS avg_salary, -- 平均薪资
SUM(salary) AS sum_salary -- 薪资总和
FROM emp;
核心考点:无GROUP BY时,聚合函数会将全表所有数据视为一个整体分组,直接输出全局统计结果;COUNT(*) 统计所有行,自动忽略NULL细节干扰。
例题二(基础):基础分组统计(单字段分组)
题目:基于员工表 emp,按部门分组,统计每个部门的员工人数、部门平均薪资,结果按部门人数降序排列。
正确写法:
sql
SELECT
dept_id,
COUNT(*) AS staff_num,
ROUND(AVG(salary),2) AS dept_avg_salary -- 保留2位小数,业务常用
FROM emp
GROUP BY dept_id
ORDER BY staff_num DESC;
解题逻辑:通过dept_id将数据划分为多个分组,再对每个分组单独执行聚合计算;最后通过ORDER BY完成排序,符合SQL执行顺序规则。
高频坑点:SELECT中查询的非聚合字段dept_id,必须写在GROUP BY之后,否则语法报错。
例题三(进阶):WHERE分组前置过滤 + HAVING分组后过滤
题目:统计在职员工中,各部门的平均薪资,只展示平均薪资大于7000的部门(在职员工状态status=1)。
错误写法:
sql
-- 错误1:用WHERE过滤聚合结果
SELECT dept_id,AVG(salary) AS avg_sal
FROM emp
WHERE status=1 AND avg_sal > 7000
GROUP BY dept_id;
-- 错误2:用HAVING过滤原始字段
SELECT dept_id,AVG(salary) AS avg_sal
FROM emp
GROUP BY dept_id
HAVING status=1 AND avg_sal > 7000;
错误原因:WHERE只能过滤原始表行数据,无法识别聚合后的字段;HAVING只能过滤分组聚合后的数据,无法过滤原始普通字段。
正确写法:
sql
SELECT
dept_id,
AVG(salary) AS avg_sal
FROM emp
WHERE status = 1 -- 第一步:先筛选在职员工(过滤原始数据)
GROUP BY dept_id -- 第二步:按部门分组
HAVING avg_sal > 7000; -- 第三步:筛选符合条件的分组
核心口诀:原始数据过滤用WHERE,分组结果过滤用HAVING。
例题四(拔高):多字段分组 + 精准业务统计
题目:基于员工表 emp,统计每个部门、每个岗位的员工人数和最高薪资,筛选出人数大于3人的部门岗位。
正确写法:
sql
SELECT
dept_id,
job,
COUNT(*) AS staff_num,
MAX(salary) AS max_sal
FROM emp
GROUP BY dept_id,job -- 多字段联合分组,字段顺序不影响结果
HAVING staff_num > 3;
解题逻辑:多字段分组规则:只有所有分组字段值完全相同的数据,才会被分为同一组,适用于精细化维度的业务统计(部门+岗位、年月+地区等)。
假设有如下数据:
| dept_id | job | salary |
|---|---|---|
| 10 | 经理 | 10000 |
| 10 | 经理 | 9000 |
| 10 | 程序员 | 8000 |
| 20 | 经理 | 9500 |
每个唯一组合会生成一行结果:
- 组1:dept_id=10, job='经理' → staff_num=2, max_sal=10000
- 组2:dept_id=10, job='程序员' → staff_num=1, max_sal=8000
- 组3:dept_id=20, job='经理' → staff_num=1, max_sal=9500
注意:
- 字段顺序不影响分组结果 :
GROUP BY dept_id, job等价于GROUP BY job, dept_id - HAVING 筛选 :只保留
staff_num > 3的组(即该部门同一岗位人数超过3人)
例题五(面试真题):COUNT统计NULL易错场景
题目:emp表中有部分员工salary(薪资)为NULL,分别统计:部门总人数、部门有薪资数据的人数。
正确写法:
sql
SELECT
dept_id,
COUNT(*) AS total_staff, -- 统计部门所有员工(含薪资NULL)
COUNT(salary) AS valid_sal_staff -- 仅统计薪资非空的员工
FROM emp
GROUP BY dept_id;
四、多表关联查询(面试高频大题)
实际业务90%的SQL都是多表查询,必须精通4种连接方式,区分JOIN和子查询的使用场景。
1. 四大JOIN区别
- INNER JOIN(内连接):只返回两张表匹配成功的数据,交集数据
- LEFT JOIN(左连接):保留左表所有数据,右表匹配不到则补NULL(业务最常用)
- RIGHT JOIN(右连接):保留右表所有数据,左表匹配不到补NULL
- FULL JOIN(全连接):保留两张表所有数据,无匹配补NULL(MySQL不支持,可用UNION替代)
2. UNION VS UNION ALL
- UNION:去重+排序,性能低
- UNION ALL:直接拼接结果,不去重、不排序,性能极高,业务优先使用
3. 子查询分类
- 标量子查询:返回单行单列,可直接用于SELECT、WHERE后
- 行子查询:返回单行多列
- 列子查询:返回多行单列,搭配IN、ANY、ALL使用
- 表子查询:返回多行多列,可作为临时表搭配JOIN使用
4. 例题精讲
统一业务表结构(所有例题通用):
员工表 emp:id(员工ID)、name(姓名)、salary(薪资)、dept_id(部门ID)
部门表 dept:dept_id(部门ID)、dept_name(部门名称)、leader_id(负责人ID)
工资表 salary_log:id、emp_id(员工ID)、month(月份)、salary(当月薪资)
| id | name | salary | dept_id |
|---|---|---|---|
| 1 | mike | 10000 | 1 |
| 2 | john | 15000 | 1 |
| 3 | black | 8000 | Null |
| dept_id | dept_name | leader_id |
|---|---|---|
| 1 | research | 2 |
| id | emp_id | month | salary |
|---|---|---|---|
| 1 | 1 | 6 | 10000 |
| 2 | 2 | 6 | 15000 |
例题一(入门):INNER JOIN 内连接基础查询
题目:查询所有有对应部门的员工姓名、部门ID、部门名称。
解题思路:只需要两表匹配成功的数据,优先使用内连接,自动过滤无部门的员工数据。
正确写法:
sql
SELECT
e.name,
e.dept_id,
d.dept_name
FROM emp e
INNER JOIN dept d
ON e.dept_id = d.dept_id;
核心考点:INNER JOIN 只返回两表匹配交集数据,员工无部门、部门无员工的数据都会被过滤,是最基础的多表关联用法。
例题二(基础):LEFT JOIN 左连接经典场景(面试必考)
题目:查询所有员工的姓名、部门名称,包含暂无分配部门的员工(无部门则部门名显示NULL)。
错误写法:
sql
-- 错误:内连接会丢失无部门的员工数据
SELECT e.name,d.dept_name
FROM emp e
JOIN dept d ON e.dept_id = d.dept_id;
正确写法:
sql
SELECT
e.name,
d.dept_name
FROM emp e
LEFT JOIN dept d
ON e.dept_id = d.dept_id;
核心考点:业务中查全量主表数据必用左连接,左表emp所有数据保留,右表无匹配字段自动补NULL。
例题三(进阶易错):LEFT JOIN 后WHERE过滤致命坑
题目:查询所有员工信息,筛选出没有对应部门的员工。
高频错误写法(90%人踩坑):
sql
-- 致命错误:WHERE过滤右表字段,抵消左连接效果,降级为内连接
SELECT *
FROM emp e
LEFT JOIN dept d ON e.dept_id = d.dept_id
WHERE d.dept_id != NULL;
错误原因:左连接查询后,右表NULL数据会被WHERE条件过滤,直接丢失主表全量数据,等同于INNER JOIN。判断无匹配数据必须用 IS NULL。
正确写法:
sql
SELECT e.*
FROM emp e
LEFT JOIN dept d
ON e.dept_id = d.dept_id
WHERE d.dept_id IS NULL; -- 筛选右表无匹配的数据
面试金句:左连接筛选右表缺失数据,只能用 IS NULL,绝对不能用!=、NOT IN。
例题四(进阶):UNION ALL 与 UNION 去重实战
题目:现有10部门员工、20部门员工两份数据,需要合并所有员工数据,不做去重、保证查询性能。
低效写法:
sql
SELECT * FROM emp WHERE dept_id=10
UNION
SELECT * FROM emp WHERE dept_id=20;
高效正确写法:
sql
SELECT * FROM emp WHERE dept_id=10
UNION ALL
SELECT * FROM emp WHERE dept_id=20;
核心考点:UNION 自带去重排序、性能极差;业务合并结果集优先 UNION ALL,仅在需要去重时使用UNION 。
例题五(拔高):三表关联综合统计
题目:统计每个部门的部门名称、员工人数、员工平均薪资,展示所有部门(含无员工的空部门)。
解题思路:部门表为主表,左连接员工表,再分组聚合,保留空部门数据。
正确写法:
sql
SELECT
d.dept_id,
d.dept_name,
COUNT(e.id) AS staff_num, -- 用员工id统计,自动忽略NULL(空部门人数为0)
ROUND(AVG(e.salary),2) AS avg_salary
FROM dept d
LEFT JOIN emp e
ON d.dept_id = e.dept_id
GROUP BY d.dept_id,d.dept_name;
关键细节:统计人数用 COUNT(员工字段) 而非COUNT(*),空部门无员工数据,字段为NULL会被自动忽略,统计结果精准为0,避免统计异常。
例题六(面试真题):子查询+关联查询嵌套实战
题目:查询2026年5月有薪资记录的员工姓名、部门名称、当月薪资。
正确写法:
sql
SELECT
e.name,
d.dept_name,
s.salary
FROM salary_log s
INNER JOIN emp e ON s.emp_id = e.id
INNER JOIN dept d ON e.dept_id = d.dept_id
WHERE s.month = '2026-05';
解题逻辑:以业务数据表(工资表)为核心,串联员工、部门表,三表内连接精准筛选有效数据,是企业数据分析最常用的多表关联结构。
五、窗口函数(进阶面试必考)
普通聚合函数是"多行进一行",窗口函数是"多行保留多行",是中高级面试、数据分析的核心难点,也是加分项。
1. 核心语法
函数() OVER (PARTITION BY 分组字段 ORDER BY 排序字段)
2. 高频窗口函数及区别
- RANK():跳跃排名(1,2,2,4),并列占用名次
- DENSE_RANK():连续排名(1,2,2,3),并列不占用名次(面试最常考)
- ROW_NUMBER():连续不重复序号(1,2,3,4),无并列
3. 常用场景
- 分组排名:每个班级成绩排名、每个部门薪资排名
- 累计统计:按月累计销售额、累计用户数
- 前后行对比:LAG()/LEAD() 获取上一行/下一行数据
4. 例题精讲
统一业务表结构(所有例题通用):
员工表 emp:id(员工ID)、name(姓名)、dept_id(部门ID)、salary(薪资)、hire_time(入职时间)
月度薪资表 salary_month:emp_id(员工ID)、month(月份)、salary(当月薪资)
例题一(入门):三大排名函数基础用法与区别
题目:查询所有员工薪资,对全员薪资进行排名,分别使用 RANK、DENSE_RANK、ROW_NUMBER 展示排名结果,对比三者差异。
正确写法:
sql
SELECT
name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
RANK() OVER (ORDER BY salary DESC) AS rk,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rk
FROM emp;
结果差异说明:
- ROW_NUMBER:无论薪资是否相同,序号连续不重复(1、2、3、4),适合取固定条数数据
- RANK:薪资相同排名相同,跳跃排名(1、2、2、4),会空缺名次
- DENSE_RANK:薪资相同排名相同,连续排名(1、2、2、3),无名次空缺,面试最常用
核心考点:面试官必问三大排名函数区别,重点掌握并列排名场景的适配场景。
例题二(基础):分区排名(分组排名核心)
题目:查询每个部门内员工的薪资排名,取出各部门员工姓名、薪资、部门内排名。
解题思路:通过 PARTITION BY 按部门分区,再在分区内按薪资排序排名,实现组内排名、组间独立。
正确写法:
sql
SELECT
dept_id,
name,
salary,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_sal_rk
FROM emp;
核心考点:PARTITION BY 是窗口函数核心,区别于普通聚合函数;分区后每组独立计算排名,互不干扰,不合并多行数据,保留原始数据行。
易错坑点:只写 ORDER BY 不写 PARTITION BY,会对全表排名,无法实现部门分组排名。
例题三(进阶):取每组TopN数据(面试高频真题)
题目:查询每个部门薪资排名前2的优秀员工(允许并列名次)。
解题思路:先通过窗口函数完成部门内排名,再通过子查询筛选排名≤2的数据,是TopN场景标准写法。
正确写法:
sql
SELECT * FROM (
SELECT
dept_id,
name,
salary,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rk
FROM emp
) t
WHERE rk <= 2;
场景选型:
- 需要保留并列名次、不遗漏数据 → 用 DENSE_RANK
- 需要严格限定2条数据、舍弃并列数据 → 用 ROW_NUMBER
例题四(进阶):LAG/LEAD 行间数据对比
题目:基于月度薪资表,查询每位员工每月薪资、上月薪资、下月薪资,用于薪资环比分析。
函数说明:
- LAG(字段,n):获取当前行向上n行数据
- LEAD(字段,n):获取当前行向下n行数据
正确写法:
sql
SELECT
emp_id,
month,
salary AS current_sal,
LAG(salary,1) OVER (PARTITION BY emp_id ORDER BY month) AS last_month_sal,
LEAD(salary,1) OVER (PARTITION BY emp_id ORDER BY month) AS next_month_sal
FROM salary_month
ORDER BY emp_id,month;
核心考点:多用于环比分析、数据差值计算、异常数据比对,是数据分析岗位高频实操场景。
例题五(拔高):累计求和统计
题目:统计每位员工从入职开始逐月累计薪资,实现薪资累计核算。
正确写法:
sql
SELECT
emp_id,
month,
salary AS month_sal,
SUM(salary) OVER (PARTITION BY emp_id ORDER BY month) AS total_acc_sal
FROM salary_month
ORDER BY emp_id,month;
语法解析:窗口函数默认从分区第一行累计到当前行,无需额外参数;搭配 ORDER BY 实现有序累计,去除 ORDER BY 则为分组整体总和。
业务场景:月度累计销售额、累计用户数、年度累计业绩等统计场景通用。
例题六(面试真题):分组去重+编号实操
题目:员工表存在同一部门多条重复薪资数据,需要对每个部门数据排序并生成唯一序号,用于删除重复数据。
正确写法:
sql
SELECT
id,
dept_id,
salary,
ROW_NUMBER() OVER (PARTITION BY dept_id, salary ORDER BY id) AS row_id
FROM emp;
- 解题逻辑:按部门+薪资双分区,对重复数据编号,后续可通过删除 row_id>1 的数据实现去重,是SQL删除重复数据的经典解法。
六、SQL优化考点(高阶面试核心)
工作3年以上面试必问,重点考察性能调优思维,拒绝慢SQL。
1. 索引优化(核心)
索引失效场景(高频背诵)
- 模糊查询以%开头:%xxx
- 索引列参与运算、函数操作:WHERE YEAR(create_time)=2026
- 使用!=、<>、NOT IN、IS NOT NULL
- 隐式类型转换:字符串字段匹配数字
- OR连接非索引字段
2. 语句优化原则
- 避免SELECT *,只查询需要的字段,减少IO开销
- 优先使用WHERE过滤数据,缩小查询范围,避免大表分组
- 多表查询小表驱动大表,LEFT JOIN 小表放左表
- 分页优化:深分页不用LIMIT 100000,10,用主键偏移查询
- 优先EXISTS,替代IN(大表场景EXISTS性能更高)
3. 避免重复计算
复杂统计使用临时表、CTE表达式,避免重复扫描大表
4. 例题精讲
统一业务表结构(所有例题通用):
员工表 emp:id(主键)、name(姓名,普通索引)、salary(薪资,普通索引)、dept_id(部门ID,普通索引)、create_time(入职时间,普通索引)、status(在职状态)
数据量级:emp表100w+测试数据,模拟线上大表慢查询场景
例题一(入门):避免索引列函数操作,解决索引失效
题目:查询2026年入职的所有员工数据,优化低效写法。
低效写法(索引失效):
sql
SELECT * FROM emp WHERE YEAR(create_time) = 2026;
错误分析:对索引列create_time使用YEAR()函数,触发索引失效规则,数据库放弃索引走全表扫描,百万级数据查询极慢。
优化后写法(区间查询、命中索引):
SELECT * FROM emp
WHERE create_time >= '2026-01-01 00:00:00'
AND create_time <= '2026-12-31 23:59:59';
核心优化思路:索引列禁止参与函数、运算操作,尽量用区间范围替代函数截取,保留索引有效性。
例题二(基础):模糊查询优化,规避左模糊索引失效
题目:查询姓名包含"磊"的员工数据,优化全模糊慢查询。
低效写法(全模糊、索引失效):
sql
SELECT * FROM emp WHERE name LIKE '%磊%';
错误分析:前后通配符全模糊查询,name索引完全失效,大表全表扫描,性能极差。
优化方案(业务优先适配):
1、业务可约束场景:改为右模糊LIKE '磊%',直接命中索引;
2、必须全模糊场景:建立全文索引或使用ES检索,替代原生模糊查询。
核心考点:日常开发坚决杜绝 %xxx、%xxx% 左模糊/全模糊查询,是线上慢SQL重灾区。
例题三(进阶):IN与EXISTS性能优化(大表场景)
题目:emp大表(100w)、dept小表(几十条),查询存在对应部门的员工数据,优化查询性能。
低效写法(大表IN查询):
sql
SELECT * FROM emp
WHERE dept_id IN (SELECT dept_id FROM dept);
性能分析:MySQL中IN对子查询结果集遍历匹配,外层大表、内层小表时,IN循环次数多、性能差。
优化后写法(EXISTS适配大表):
sql
SELECT * FROM emp e
WHERE EXISTS (SELECT 1 FROM dept d WHERE e.dept_id = d.dept_id);
核心优化口诀:外层大表用EXISTS,外层小表用IN,精准适配不同数据量级。
例题四(进阶易错):隐式类型转换索引失效优化
题目:dept_id为字符串索引字段,查询部门编号为10的员工数据,优化失效查询。
低效写法(隐式类型转换):
sql
SELECT * FROM emp WHERE dept_id = 10;
错误分析:字符串索引字段与数字匹配,触发隐式类型转换,数据库会对全表字段统一转数字,索引彻底失效,极易被忽略。
优化后写法(类型一致、命中索引):
sql
SELECT * FROM emp WHERE dept_id = '10';
核心考点:查询条件必须字段与参数类型完全一致,杜绝隐式转换。
例题五(拔高):深分页LIMIT超慢查询优化
题目:查询emp表第100000页之后的10条数据(深分页场景),优化慢查询。
低效写法(经典深分页坑):
sql
SELECT * FROM emp ORDER BY id LIMIT 100000,10;
错误分析:LIMIT偏移量过大时,数据库会先查询前100010条数据,丢弃前100000条,只保留10条,数据量越大,耗时指数级增长。
补充:
LIMIT 关键字在 SQL 中用于限制查询结果返回的行数。
它的核心作用有两个:
- 获取前 N 条记录(例如:查询分数最高的前 10 名学生)
- 实现分页功能(例如:每页显示 20 条,查看第 3 页的数据)
sql
-- 返回前 5 行
SELECT * FROM 表名 LIMIT 5;
-- 跳过前 5 行,然后返回 10 行(用于分页)
SELECT * FROM 表名 LIMIT 5, 10;
-- 或者使用 OFFSET 写法(更清晰)
SELECT * FROM 表名 LIMIT 10 OFFSET 5;
优化后写法(主键偏移分页):
sql
-- 先查上一页最大ID,再偏移查询
SELECT * FROM emp WHERE id > 100000 ORDER BY id LIMIT 10;
优化原理:利用主键索引有序性,直接定位起始位置,无需扫描前置数据,查询耗时从秒级降至毫秒级。
适用场景:所有需要分页展示的列表数据,尤其后台管理系统、数据报表。
例题六(面试真题):SELECT * 冗余查询优化
题目:查询在职员工的姓名、薪资、入职时间,优化冗余SQL。
低效写法(全字段查询):
sql
SELECT * FROM emp WHERE status = 1;
错误分析:SELECT * 会查询所有字段,包含大文本、冗余字段,增加磁盘IO、网络传输开销,无法使用覆盖索引。
优化后写法(按需查字段+覆盖索引):
sql
SELECT name,salary,create_time FROM emp WHERE status = 1;
- 进阶优化:可建立联合索引 idx_status_name_salary,直接从索引获取数据,无需回表查询,性能拉满。
七、面试高频坑点(避坑指南)
很多人基础不错,但总栽在细节坑点上,这些是面试官最爱考察的细节!
- NULL坑:任何值和NULL运算结果都是NULL,聚合函数自动忽略NULL
- GROUP BY坑:分组后筛选必须用HAVING,WHERE无法识别聚合结果
- JOIN坑:左连接后用WHERE过滤右表字段,会强制抵消左连接效果,等同于内连接
- 排序坑:ORDER BY 字段默认升序,NULL值默认排在最前面
- 去重坑:DISTINCT 是对整行去重,不是单个字段,且性能较差
八、高频面试真题场景总结
- 统计每日新增用户、每日活跃用户、留存用户(窗口函数+分组)
- 找出重复数据、删除重复数据(GROUP BY、ROW_NUMBER)
- 学生成绩排名、科目最高分(DENSE_RANK、PARTITION BY)
- 多表关联统计订单、用户、销售额数据(LEFT JOIN + 聚合)
- 慢SQL排查与优化思路(索引+语句+业务优化)
最后总结
SQL面试的核心逻辑:基础语法不踩坑、分组关联熟练用、窗口函数会进阶、优化思路有逻辑。
大部分初级面试只考基础查询、多表关联、分组统计;中高级面试重点考察窗口函数、SQL优化、业务场景落地。