SQL 基础面试考点总结

吃透这些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 中用于限制查询结果返回的行数。

它的核心作用有两个:

  1. 获取前 N 条记录(例如:查询分数最高的前 10 名学生)
  2. 实现分页功能(例如:每页显示 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,直接从索引获取数据,无需回表查询,性能拉满。

七、面试高频坑点(避坑指南)

很多人基础不错,但总栽在细节坑点上,这些是面试官最爱考察的细节!

  1. NULL坑:任何值和NULL运算结果都是NULL,聚合函数自动忽略NULL
  2. GROUP BY坑:分组后筛选必须用HAVING,WHERE无法识别聚合结果
  3. JOIN坑:左连接后用WHERE过滤右表字段,会强制抵消左连接效果,等同于内连接
  4. 排序坑:ORDER BY 字段默认升序,NULL值默认排在最前面
  5. 去重坑:DISTINCT 是对整行去重,不是单个字段,且性能较差

八、高频面试真题场景总结

  1. 统计每日新增用户、每日活跃用户、留存用户(窗口函数+分组)
  2. 找出重复数据、删除重复数据(GROUP BY、ROW_NUMBER)
  3. 学生成绩排名、科目最高分(DENSE_RANK、PARTITION BY)
  4. 多表关联统计订单、用户、销售额数据(LEFT JOIN + 聚合)
  5. 慢SQL排查与优化思路(索引+语句+业务优化)

最后总结

SQL面试的核心逻辑:基础语法不踩坑、分组关联熟练用、窗口函数会进阶、优化思路有逻辑。

大部分初级面试只考基础查询、多表关联、分组统计;中高级面试重点考察窗口函数、SQL优化、业务场景落地。

相关推荐
深蓝轨迹12 小时前
缓存雪崩终极防御:Caffeine + Redis 多级缓存
数据库·redis·缓存·caffine
新时代农民工~13 小时前
PostgreSQL 主从故障恢复自动化:实战脚本与最佳实践
数据库·postgresql·自动化
woshilys13 小时前
sql server 查询外键
数据库·sql·sqlserver
Skr-Eric14 小时前
面试复盘7.0
面试·职场和发展
ricardo197314 小时前
浏览器渲染流水线:从 HTML 到屏幕上的像素
前端·面试
瀚高PG实验室14 小时前
开发管理工具打不开No way to find ori gi nal streamhand er for jar protocol
java·数据库·jar·瀚高数据库
__zRainy__14 小时前
Redis系列:缓存抽象封装与最佳实践
数据库·redis·缓存
cen__y14 小时前
Linux13(数据库)
linux·服务器·c语言·开发语言·数据库
__zRainy__14 小时前
Redis系列:核心数据类型与基础 API 解读
数据库·redis·缓存