SQL 快速学习笔记:1~2 小时掌握面试重点

0. 使用说明

  • 用法:先记每节「含义」,再看「例子」。
  • 目标:面试能手写左右连接、分组、子查询、窗口函数、分页;能说清执行计划和索引。

1. 基础:增删改查 + 事务

语句 含义 例子
SELECT 查询 SELECT * FROM emp WHERE deptno=10;
INSERT 插入 INSERT INTO dept(deptno,dname) VALUES(50,'测试');
UPDATE 更新 UPDATE emp SET sal=sal*1.1 WHERE deptno=20;
DELETE 删除 DELETE FROM emp WHERE empno=8888;
DDL 建表/改表/删表 CREATE TABLE t(id INT); ALTER TABLE t ADD name VARCHAR(20); DROP TABLE t;
DML 增删改查 SELECT / INSERT / UPDATE / DELETE
事务 要么全成功,要么全回滚 COMMIT; ROLLBACK; SAVEPOINT sp1;

重点

  • DELETE 可回滚,TRUNCATE 不可回滚且快。
  • WHERE 必须写,否则全表更新/删除。
  • 事务 ACID:原子、一致、隔离、持久。

2. 连接:JOIN 全家桶

准备:dept(deptno,dname),emp(empno,ename,deptno,sal,mgr)

2.1 四种连接

类型 含义 例子
INNER JOIN 只保留两边都匹配 SELECT e.ename,d.dname FROM emp e JOIN dept d ON e.deptno=d.deptno;
LEFT JOIN 左表全保留,右表没有补 NULL SELECT d.dname,e.ename FROM dept d LEFT JOIN emp e ON d.deptno=e.deptno;
RIGHT JOIN 右表全保留,左表没有补 NULL SELECT d.dname,e.ename FROM dept d RIGHT JOIN emp e ON d.deptno=e.deptno;
FULL OUTER JOIN 两边都保留 SELECT d.dname,e.ename FROM dept d FULL OUTER JOIN emp e ON d.deptno=e.deptno;

2.2 Oracle 老式外连接 (+)

(+) 放在可能缺数据的那张表列上。

sql 复制代码
-- 保留 dept 所有部门,emp 没匹配补 NULL
SELECT d.dname,e.ename
FROM dept d, emp e
WHERE d.deptno = e.deptno(+);

-- 保留 emp 所有员工,dept 没匹配补 NULL
SELECT e.ename,d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno(+);

注意:(+) 不能和 ANSI 的 LEFT JOIN 混用。

2.3 自连接

同一张表起两个别名,查层级关系。

sql 复制代码
SELECT e.ename AS 员工, m.ename AS 经理
FROM emp e
LEFT JOIN emp m ON e.mgr = m.empno;

2.4 多表连接

sql 复制代码
SELECT e.ename,d.dname,l.city
FROM emp e
JOIN dept d ON e.deptno=d.deptno
JOIN location l ON d.loc_id=l.loc_id;

口诀

  • INNER 只匹配,LEFT 保左,RIGHT 保右,FULL 两边保。
  • 找「左表独有」:LEFT JOIN ... WHERE 右表主键 IS NULL。
  • 找「右表独有」:RIGHT JOIN ... WHERE 左表主键 IS NULL。

3. 复杂查询

3.1 子查询

sql 复制代码
-- 工资高于平均工资的人
SELECT ename,sal FROM emp
WHERE sal > (SELECT AVG(sal) FROM emp);

3.2 相关子查询 + EXISTS / IN

sql 复制代码
-- 有员工的部门
SELECT * FROM dept d
WHERE EXISTS (SELECT 1 FROM emp e WHERE e.deptno=d.deptno);

-- 没有员工的部门
SELECT * FROM dept d
WHERE NOT EXISTS (SELECT 1 FROM emp e WHERE e.deptno=d.deptno);

重点

  • IN 适合子查询结果小。
  • EXISTS 适合大表,找到一条就返回。
  • NOT IN 子查询有 NULL 会出问题,优先用 NOT EXISTS。

3.3 集合运算

运算 含义 例子
UNION 并集,去重 SELECT deptno FROM emp UNION SELECT deptno FROM dept;
UNION ALL 并集,不去重 同上,速度快
INTERSECT 交集 SELECT deptno FROM emp INTERSECT SELECT deptno FROM dept;
MINUS 差集,在 A 不在 B SELECT deptno FROM dept MINUS SELECT deptno FROM emp;

3.4 GROUP BY / HAVING / 聚合

sql 复制代码
SELECT deptno, COUNT(*) AS 人数, AVG(sal) AS 平均工资
FROM emp
GROUP BY deptno
HAVING COUNT(*) >= 2;

重点

  • WHERE 过滤行,HAVING 过滤组。
  • COUNT(*) 算所有行,COUNT(列) 忽略 NULL。
  • 没有 GROUP BY 时,聚合函数把整表当一组。

3.5 CASE / DECODE / NVL / COALESCE

sql 复制代码
-- CASE:CASE WHEN 是条件判断
SELECT ename,
       CASE WHEN sal>=3000 THEN '高'
            WHEN sal>=1500 THEN '中'
            ELSE '低' END AS 工资等级
FROM emp;

-- DECODE(Oracle):等值映射
SELECT ename, DECODE(deptno,10,'会计',20,'研发','其他') FROM emp;

-- NVL / COALESCE 空值替换
SELECT NVL(comm,0) FROM emp;          -- Oracle
SELECT COALESCE(comm,0) FROM emp;     -- 标准 SQL

4. 窗口函数

语法:

sql 复制代码
函数() OVER (PARTITION BY 分组列 ORDER BY 排序列)

PARTITION BY = 窗口里的分组,不减少行。

4.1 排名:ROW_NUMBER / RANK / DENSE_RANK

sql 复制代码
SELECT deptno,ename,sal,
       ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) rn,
       RANK()       OVER (PARTITION BY deptno ORDER BY sal DESC) rk,
       DENSE_RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) drk
FROM emp;
函数 并列时
ROW_NUMBER 1,2,3 不重复
RANK 1,1,3 跳号
DENSE_RANK 1,1,2 不跳号

每个部门工资前 3:

sql 复制代码
SELECT deptno,ename,sal
FROM (
  SELECT deptno,ename,sal,
         ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) rn
  FROM emp
) t
WHERE rn <= 3;

4.2 SUM OVER 累计 / 分组汇总

sql 复制代码
-- 每行工资占部门总工资比例
SELECT deptno,ename,sal,
       SUM(sal) OVER (PARTITION BY deptno) AS 部门总工资,
       SUM(sal) OVER (PARTITION BY deptno ORDER BY hiredate) AS 累计工资
FROM emp;

4.3 LAG / LEAD 前后行

sql 复制代码
SELECT ename,sal,
       LAG(sal)  OVER (ORDER BY sal) AS 上一名工资,
       LEAD(sal) OVER (ORDER BY sal) AS 下一名工资
FROM emp;

5. CTE 和递归

5.1 CTE(WITH)

sql 复制代码
WITH dept_avg AS (
  SELECT deptno, AVG(sal) avg_sal
  FROM emp
  GROUP BY deptno
)
SELECT e.ename,e.sal,d.avg_sal
FROM emp e
JOIN dept_avg d ON e.deptno=d.deptno
WHERE e.sal > d.avg_sal;

5.2 递归

标准 SQL:

sql 复制代码
WITH RECURSIVE nums(n) AS (
  SELECT 1
  UNION ALL
  SELECT n+1 FROM nums WHERE n<5
)
SELECT * FROM nums;

Oracle 老写法:

sql 复制代码
SELECT LEVEL FROM dual CONNECT BY LEVEL <= 5;

6. 分页

Oracle ROWNUM

sql 复制代码
SELECT *
FROM (
  SELECT e.*, ROWNUM rn
  FROM (SELECT * FROM emp ORDER BY sal DESC) e
  WHERE ROWNUM <= 20
)
WHERE rn > 10;

标准 OFFSET FETCH

sql 复制代码
SELECT * FROM emp
ORDER BY sal DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

MySQL

sql 复制代码
SELECT * FROM emp ORDER BY sal DESC LIMIT 10 OFFSET 10;

重点:分页必须先排序,再取范围。

7. 日期 / 字符串函数

类型 Oracle MySQL / 标准
当前时间 SYSDATE NOW()
格式化 TO_CHAR(SYSDATE,'YYYY-MM-DD') DATE_FORMAT(NOW(),'%Y-%m-%d')
转日期 TO_DATE('2024-01-01','YYYY-MM-DD') STR_TO_DATE
加减月 ADD_MONTHS(SYSDATE,1) DATE_ADD
取年 EXTRACT(YEAR FROM SYSDATE) YEAR(NOW())
截取 SUBSTR('abcdef',1,3) SUBSTRING
查找 INSTR('abcdef','c') LOCATE
拼接 CONCAT('a','b') 或 `
去空格 TRIM(' a ') TRIM
大小写 UPPER / LOWER 同
替换 REPLACE('abc','b','x') 同
长度 LENGTH('abc') CHAR_LENGTH

8. 性能:执行计划、索引、SQL 重写

8.1 执行计划

sql 复制代码
EXPLAIN PLAN FOR
SELECT * FROM emp WHERE deptno=10;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

看:全表扫描、索引扫描、连接方式、成本。

8.2 索引

  • 在 WHERE、JOIN、ORDER BY 常用列建索引。
  • 避免在索引列上使用函数:WHERE TO_CHAR(date_col)='2024' 会失效。
  • 复合索引注意最左前缀。
  • 索引不是越多越好,影响插入更新。

8.3 统计信息

sql 复制代码
-- Oracle
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA','EMP');

统计信息新鲜,优化器才能选对执行计划。

8.4 避免全表扫描 / SQL 重写

  • 只取需要列,别 SELECT *。
  • 用绑定变量,减少硬解析。
  • 大表分区。
  • EXISTS 代替 IN(大表)。
  • UNION ALL 代替 UNION(不需要去重时)。
  • 子查询改 JOIN。
  • 分页先排序再取范围。

9. 面试手写速练

9.1 左右连接

sql 复制代码
-- 所有部门及员工
SELECT d.dname,e.ename
FROM dept d LEFT JOIN emp e ON d.deptno=e.deptno;

-- 没有员工的部门
SELECT d.*
FROM dept d LEFT JOIN emp e ON d.deptno=e.deptno
WHERE e.empno IS NULL;

-- 没有部门的员工
SELECT e.*
FROM emp e LEFT JOIN dept d ON e.deptno=d.deptno
WHERE d.deptno IS NULL;

9.2 多表 + 分组 + 子查询

sql 复制代码
SELECT d.dname, COUNT(e.empno) AS 人数, AVG(e.sal) AS 平均工资
FROM dept d
LEFT JOIN emp e ON d.deptno=e.deptno
GROUP BY d.dname;

9.3 窗口函数排名、累计

sql 复制代码
-- 每个部门工资前 3
SELECT deptno,ename,sal
FROM (
  SELECT deptno,ename,sal,
         ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) rn
  FROM emp
) t
WHERE rn <= 3;

-- 累计工资
SELECT ename,sal,
       SUM(sal) OVER (ORDER BY hiredate) AS 累计
FROM emp;

9.4 分页

sql 复制代码
SELECT * FROM (
  SELECT e.*, ROWNUM rn
  FROM (SELECT * FROM emp ORDER BY sal DESC) e
  WHERE ROWNUM <= 20
)
WHERE rn > 10;

10. 一句话口诀

  • 连接:INNER 只匹配,LEFT 保左,RIGHT 保右,FULL 两边保。
  • 外连接条件 :右表条件放 ON 保左表;放 WHERE 退化成内连接。
  • 分组 :WHERE 过滤行,HAVING 过滤组。
  • 窗口 :PARTITION BY 分组不合并行,ROW_NUMBER 不重复,RANK 跳号,DENSE_RANK 不跳。
  • 分页:先排序,再取范围。
  • 性能 :看执行计划,建索引,避函数,用绑定变量,少 SELECT *。

1 小时过一遍,2 小时把第 9 节手写一遍,面试重点基本覆盖。

相关推荐
Hotchip_MEMS2 小时前
从触发吸气道智能感知:MEMS硅麦埋下雾化器智能化伏笔
大数据·人工智能·笔记·电脑·制造
fanged2 小时前
Audio学习4(DSP)(TODO)
学习
泡海椒2 小时前
JQuick-SQL CASE WHEN 与函数:在查询中构建业务字段
数据库·sql
波力海苔夹心脆6752 小时前
C# StringBuilder 详解:字符串拼接为什么总说要用它?
开发语言·笔记·c#·.net·字符串·stringbuilder·入门教程
weixin_448119942 小时前
Datawhale DeepSeek Harness 技术入门与架构原理 笔记3
笔记·架构
lengxuenong2 小时前
树状数组 · 信息学学习手册3-优化LIS
c++·学习·算法
果壳science2 小时前
关于光子、量子猜想
笔记·科技·学习·创业创新·学习方法
愤怒火龙果3 小时前
蓝队笔记整理01 C2 远控与 Webshell 流量特征
笔记·网络安全
小弥儿3 小时前
GitHub今日热榜 | 2026-10-22:智能体工具退潮,基础层集中上新
学习·开源·github