SQL 从入门到进阶:语法速查与实战练习(完整优化版)

SQL 关键字速查总表

本文所有 SQL 关键字的中英文对照与作用速查,按主题分类整理。复习时先扫表建立索引,再回看正文加深记忆。

一、查询与条件筛选

关键字 中文 作用
SELECT 查询、选择 指定要返回的列
FROM 从 指定数据来源表
AS 作为、别名 给列或表起别名
WHERE 在哪里、条件 按条件筛选数据行
AND 与、并且 多个条件同时满足
OR 或、或者 多个条件满足其一
NOT 非、取反 对条件取反
BETWEEN ... AND ... 在......之间 判断区间范围(包含边界)
IN 在......之中 匹配集合中的任意一个值
LIKE 像、模糊匹配 按模式做模糊查询
IS NULL 为空 判断字段值为 NULL
IS NOT NULL 非空 判断字段值不为 NULL
二、结果处理与排序 ORDER BY
关键字 中文 作用
DISTINCT 去重 对查询结果去重
ORDER BY 按......排序 对结果集排序
ASC 升序 从小到大排序(默认)
DESC 降序 从大到小排序
LIMIT 限制、截断 截取指定条数结果
OFFSET 偏移 跳过若干行再取数
CASE WHEN THEN ELSE END 条件分支 类似 if-else,按条件生成新列

三、聚合与分组 GROUP BY

关键字 中文 作用
COUNT 计数 统计行数或非空值数量
SUM 求和 计算数值列之和
AVG 平均值 计算数值列平均
MAX 最大值 求最大值
MIN 最小值 求最小值
GROUP BY 分组 按字段分组后聚合
HAVING 分组后筛选 过滤分组结果
GROUP_CONCAT 分组拼接字符串 把组内多行值拼成一个串
WITH ROLLUP 小计与总计 追加小计行和总计行

四、关联查询 JOIN

关键字 中文 作用
JOIN / INNER JOIN 内连接 取两表能匹配的交集
LEFT JOIN 左连接 保留左表全部行
RIGHT JOIN 右连接 保留右表全部行
FULL JOIN 全连接 两表全部保留
CROSS JOIN 交叉连接 无条件笛卡尔积
ON 在......条件上 指定关联匹配条件

五、子查询与集合运算

关键字 中文 作用
EXISTS 存在 判断子查询是否存在匹配行
NOT EXISTS 不存在 判断子查询无匹配行
ANY 任意 满足子查询结果中任意一个即可
ALL 全部 必须满足子查询结果中所有值
UNION 并集 合并结果集并去重
UNION ALL 并集不去重 合并结果集保留重复行
INTERSECT 交集 取两个结果集的公共行
EXCEPT 差集 取前一个结果集独有行

六、开窗函数

关键字 中文 作用
OVER 窗口 定义开窗计算范围
PARTITION BY 按......分区 窗口内分组
ORDER BY 按......排序 窗口内排序
ROW_NUMBER 行号 分配连续唯一序号,不跳号
RANK 排名(跳号) 并列同名次且跳号(1、1、3)
DENSE_RANK 密集排名(不跳号) 并列同名次且不跳号(1、1、2)
NTILE 分桶 把结果平均分成 n 桶并编号
LAG 滞后 取当前行之前某行的值
LEAD 前导 取当前行之后某行的值
FIRST_VALUE 首值 取窗口内首行的值
LAST_VALUE 末值 取窗口内末行的值
ROWS BETWEEN 行范围 限定窗口帧范围
UNBOUNDED PRECEDING 组首 窗口帧从分组第一行开始
CURRENT ROW 当前行 窗口帧到当前行

七、常用函数 常用函数

关键字 中文 作用
UPPER / LOWER 大写 / 小写 字符串大小写转换
LENGTH / CHAR_LENGTH 长度 返回字节 / 字符长度
CONCAT / CONCAT_WS 拼接 连接多个字符串
REPLACE 替换 把子串替换为新串
SUBSTRING 截取子串 按位置截取子串
LEFT / RIGHT 左截取 / 右截取 截取左 / 右 n 个字符
TRIM / LTRIM / RTRIM 去空格 去除首尾 / 左 / 右空格
INSTR / LOCATE 定位子串 返回子串首次出现位置
LPAD / RPAD 左补齐 / 右补齐 把字符串补齐到指定长度
REVERSE / REPEAT 反转 / 重复 反转字符串 / 重复 n 次
ROUND 四舍五入 对数值四舍五入
CEIL / FLOOR 向上取整 / 向下取整 取整运算
ABS 绝对值 取绝对值
MOD 取余 求余数
POWER 幂运算 求幂
DATE / DATETIME / TIME 日期 / 日期时间 / 时间 获取当前日期、日期时间、时间
NOW / CURDATE 当前时间 / 当前日期 获取当前日期时间 / 当前日期
DATE_FORMAT 日期格式化 格式化日期输出
DATEDIFF 日期差 计算两个日期相差天数
DATE_ADD / DATE_SUB 日期加 / 日期减 对日期做加减运算
YEAR / MONTH / DAY 年 / 月 / 日 提取日期的年、月、日
IFNULL 空值替换 值为 NULL 时返回默认值
COALESCE 取首个非空 返回第一个非 NULL 值
NULLIF 相等转 NULL 两值相等返回 NULL,否则返回值 1
CAST / CONVERT 类型转换 显式转换数据类型

八、数据写入与表结构

关键字 中文 作用
INSERT INTO 插入到 向表插入新数据行
VALUES 值 指定插入的具体值
UPDATE 更新 修改表中已有数据
SET 设置 指定要修改的列和新值
DELETE 删除 删除满足条件的行
TRUNCATE 清空 快速清空整表
CREATE TABLE 创建表 新建数据表
ALTER TABLE 修改表 变更表结构
ADD COLUMN 添加列 新增一列
DROP COLUMN 删除列 删除已有列
MODIFY COLUMN 修改列 修改列类型或属性
RENAME TO 重命名 修改表名
DROP 删除对象 删除表、视图、索引等
PRIMARY KEY 主键 唯一标识且非空
UNIQUE 唯一 值不允许重复
NOT NULL 非空 值不允许为空
DEFAULT 默认值 未指定值时的缺省值
FOREIGN KEY 外键 关联另一张表的列
REFERENCES 引用 指定外键引用的表和列
CHECK 检查 检查数据满足约束条件
CREATE INDEX 创建索引 提高查询速度
CREATE VIEW 创建视图 把查询保存为虚拟表
BEGIN / COMMIT / ROLLBACK 开启 / 提交 / 回滚 事务控制
CREATE USER 创建用户 新建数据库用户
GRANT / REVOKE 授权 / 回收权限 分配 / 回收用户权限
AUTO_INCREMENT 自增 自动递增生成编号

九、进阶查询

关键字 中文 作用
WITH 公共表表达式 CTE 给查询命名成临时结果集
WITH RECURSIVE 递归查询 递归处理树形结构
EXPLAIN 执行计划 查看 SQL 执行计划,定位慢查询

SQL 基础

假定当前有一个数据库表 student,包含字段 name、age、score 等,后续示例均基于此表演示。

参考 SQL之母

一、基础查询 SELECT

1、全表查询 SELECT *

使用 SELECT * FROM 表名 这样的 SQL 语句时,就是在进行全表查询。* 代表所有列。

sql 复制代码
SELECT * FROM 表名;
sql 复制代码
SELECT * FROM student;
2、选择查询 SELECT

从数据表中选择特定的列或字段,并只返回这些列的数据值,语法为 SELECT 列名1, 列名2, ... FROM 表名。

sql 复制代码
SELECT 列名1, 列名2, ...
FROM 表名;

从 student 表中选出所有学生的姓名(name)和年龄(age):

sql 复制代码
SELECT name, age FROM student;
3、别名 AS

使用别名语法 {原始字段名} AS {别名} 来为查询结果的列名取一个便于理解的名称。

sql 复制代码
SELECT 列名 AS 别名 [, 列名 AS 别名 ...] FROM 表名;

从 student 表查询学生姓名(name)和年龄(age),并分别取别名为「学生姓名」和「学生年龄」:

sql 复制代码
SELECT name AS '学生姓名', age AS '学生年龄' FROM student;
4、常量和运算 +、-、*、/ 算术运算符

在查询语句中可以对数据进行加减乘除、取平均值、连接文本等操作。

sql 复制代码
SELECT 列名, 列名 + 值 AS 别名, 列名 - 值 AS 别名, 列名 * 值 AS 别名, 列名 / 值 AS 别名 FROM 表名;

从 student 表中选出所有学生的姓名(name)和分数(score),并额外计算出分数的 2 倍:

sql 复制代码
SELECT name, score, score * 2 AS double_score FROM student;

二、条件查询 WHERE

5、WHERE 条件筛选 WHERE

从数据库中筛选出满足特定条件的数据行。
从 student 表中选出学生姓名(name)和成绩(score),要求姓名等于 'L':

sql 复制代码
SELECT 列名 FROM 表名 WHERE 条件;
```> 从 `student` 表中选出学生姓名(name)和成绩(score),要求姓名等于 `'L'`:

```sql
SELECT name, score FROM student WHERE name = 'L';
6、运算符 =、!=、>=、<、>、BETWEEN

>=、!=、<、> 等运算符可根据字段的不同取值进行灵活的筛选和过滤;区间判断可使用 BETWEEN A AND B,表示落在 A 到 B 范围内(包含边界)。

sql 复制代码
SELECT 列名
FROM 表名
WHERE 列名 >= 值
   OR 列名 != 值
   OR 列名 < 值
   OR 列名 > 值
   OR 列名 BETWEEN 值 AND 值; -- 区间判断,包含边界

从 student 表中选出所有学生的姓名(name)和年龄(age),要求姓名不等于 'L':

sql 复制代码
SELECT name, age FROM student WHERE name != 'L';
7、空值判断 IS NULL、IS NOT NULL

使用 IS NULL、IS NOT NULL 判断字段是否为空值或非空值。注意:判断空值不能用 = NULL。

sql 复制代码
SELECT 列名 FROM 表名 WHERE 列名 IS NULL;
SELECT 列名 FROM 表名 WHERE 列名 IS NOT NULL;

从 student 表中选出姓名(name)、年龄(age)和成绩(score),要求年龄不为空:> 从 student 表中选出姓名(name)、年龄(age)和成绩(score),要求年龄不为空:

sql 复制代码
SELECT name, age, score FROM student WHERE age IS NOT NULL;
8、模糊查询 LIKE

使用 LIKE 关键字实现模糊查询,常用通配符:

  • 百分号 %:表示任意长度的任意字符序列。
  • 下划线 _:表示任意单个字符。
sql 复制代码
SELECT 列名 FROM 表名 WHERE 列名 [NOT] LIKE 匹配模式;

从 student 表中选出学生姓名(name)和成绩(score),要求姓名不包含 "L":> 从 student 表中选出学生姓名(name)和成绩(score),要求姓名不包含 "L":

sql 复制代码
SELECT name, score FROM student WHERE name NOT LIKE '%L%';
9、逻辑运算 AND、OR、NOT

使用 AND、OR、NOT 组合多个条件:AND 同时满足、OR 满足其一、NOT 取反。

sql 复制代码
SELECT 列名
FROM 表名
WHERE 条件1 AND 条件2;

SELECT 列名
FROM 表名
WHERE 条件1 OR 条件2;

SELECT 列名
FROM 表名
WHERE NOT 条件;
```> 使用 `AND`、`OR`、`NOT` 组合多个条件:`AND` 同时满足、`OR` 满足其一、`NOT` 取反。

> 从 `student` 表中选出学生姓名(name)和成绩(score),要求姓名包含 `"L"`,或者成绩大于 500:

```sql
SELECT name, score
FROM student
WHERE name LIKE '%L%' OR score > 500;

三、结果处理 DISTINCT/ORDER BY/LIMIT

10、去重 DISTINCT

使用 DISTINCT 关键字对查询结果去重。当作用于多个列时,去重的是多列组合。

sql 复制代码
SELECT DISTINCT 列名1, 列名2, ... FROM 表名;

从 student 表中查询所有不重复的班级 ID(class_id)和考试编号(exam_num)组合:> 从 student 表中查询所有不重复的班级 ID(class_id)和考试编号(exam_num)组合:

sql 复制代码
SELECT DISTINCT class_id, exam_num FROM student;
11、排序 ORDER BY 【ASC / DESC】

ORDER BY 用于结果排序:ASC 升序(从小到大,默认)、DESC 降序(从大到小)。多字段排序语法为 ORDER BY 字段1 [ASC|DESC], 字段2 [ASC|DESC], ...。

sql 复制代码
SELECT 列名
FROM 表名
[WHERE 条件]
ORDER BY 排序字段 [ASC|DESC], 排序字段 [ASC|DESC];

查询学生姓名(name)、年龄(age)和成绩(score),按成绩从大到小排序,成绩相同时按年龄从小到大排序:

sql 复制代码
SELECT name, age, score
FROM student
ORDER BY score DESC, age ASC;
13、截断和偏移 LIMIT

LIMIT 用于截断查询结果:

  • LIMIT m:只取前 m 条。
  • LIMIT n, m:跳过前 n 条后,再取 m 条(n 从 0 开始计数)。
sql 复制代码
-- 通用语法模板
SELECT 列名
FROM 表名
[WHERE 条件]
[ORDER BY 排序字段 [ASC|DESC]]
LIMIT 条数;          -- 只取前 N 条

SELECT 列名
FROM 表名
[WHERE 条件]
[ORDER BY 排序字段 [ASC|DESC]]
LIMIT 偏移量, 条数;  -- 跳过前 N 条后,再取 M 条

从 student 表查询学生姓名(name)和年龄(age),按年龄从小到大排序,从第 2 条开始截取 3 个学生:

sql 复制代码
SELECT name, age
FROM student
ORDER BY age ASC
LIMIT 1, 3;
14、条件分支 CASE WHEN THEN ELSE END

CASE WHEN 类似编程语言中的 if else 条件判断,可根据条件动态生成新列或对现有列进行转换。语法模板:

sql 复制代码
SELECT
  列名,
  CASE
    WHEN (条件1) THEN 结果1
    WHEN (条件2) THEN 结果2
    [WHEN (条件N) THEN 结果N ...]
    [ELSE 其他结果]
  END [AS 别名]
FROM 表名
[WHERE 条件]
[ORDER BY 排序字段 [ASC|DESC]];

将 student 表按年龄划分为三个等级(age_level):60 岁以上为「老同学」,20 岁以上(不含 60 岁以上)为「年轻」,20 岁及以下或没有年龄信息为「小同学」。返回学生姓名(name)和年龄等级(age_level),并按姓名升序排序:

sql 复制代码
SELECT
  name,
  CASE
    WHEN (age > 60) THEN '老同学'
    WHEN (age > 20) THEN '年轻'
    ELSE '小同学'
  END AS age_level
FROM student
ORDER BY name ASC;
38、分页查询 LIMIT 与排序稳定性 LIMIT

分页本质是 ORDER BY + LIMIT 的组合。排序字段有重复值时结果顺序不稳定,建议追加唯一列(如 id)作为兜底排序,保证翻页不重复、不遗漏。

sql 复制代码
SELECT 列
FROM 表
[WHERE 条件]
ORDER BY 排序字段 [ASC|DESC], 唯一列 [ASC|DESC]
LIMIT 偏移量, 条数;

student 表包含 id、name、score。按成绩降序分页,每页 10 条,取第 2 页:

sql 复制代码
SELECT id, name, score
FROM student
ORDER BY score DESC, id ASC
LIMIT 10, 10;
四、函数 UPPER/LOWER/CONCAT/SUBSTRING/ROUND/CEIL/FLOOR/ABS/DATE_FORMAT/DATEDIFF
15、时间函数 DATE()、DATETIME()、TIME()

常用时间函数:

  • DATE():获取当前日期(如 2023-08-01)。
  • DATETIME():获取当前日期时间(如 2023-08-01 14:30:00)。
  • TIME():获取当前时间(如 14:30:00)。

查询所有学生姓名(name)和当前日期,列名为「当前日期」:

sql 复制代码
SELECT 列名, DATE() AS '别名' FROM 表名;
SELECT 列名, DATETIME() AS '别名' FROM 表名;
SELECT 列名, TIME() AS '别名' FROM 表名;
sql 复制代码
SELECT name, DATE() AS '当前日期' FROM student;
16、字符串处理函数 UPPER/LOWER/CONCAT/SUBSTRING

常用字符串函数

  • UPPER(str):把字符串全部转换为大写。
  • LOWER(str):把字符串全部转换为小写。
  • LENGTH(str):返回字符串长度(字节数因数据库实现而异)。
  • CHAR_LENGTH(str):返回字符个数,中文等多字节字符按 1 个字符计算。
  • CONCAT(str1, str2, ...):拼接多个字符串;任一参数为 NULL 时,结果通常为 NULL。
  • CONCAT_WS(sep, str1, str2, ...):用分隔符 sep 拼接多个字符串,自动跳过 NULL。
  • REPLACE(str, from_str, to_str):把字符串中的 from_str 替换为 to_str。
  • SUBSTRING(str, pos, len):从 pos 位置开始截取 len 长度的子串,pos 从 1 开始。
  • LEFT(str, len) / RIGHT(str, len):分别截取字符串左侧 / 右侧 len 个字符。
  • TRIM(str):去掉字符串首尾空格;LTRIM / RTRIM 分别只去左侧 / 右侧空格。
  • INSTR(str, substr):返回子串 substr 在 str 中首次出现的位置,找不到返回 0。
  • LOCATE(substr, str):与 INSTR 作用相同,参数顺序相反。
  • LPAD(str, len, pad) / RPAD(str, len, pad):用 pad 在左 / 右侧把字符串补齐到 len 长度。
  • REVERSE(str):反转字符串。
  • REPEAT(str, n):把字符串重复 n 次。
sql 复制代码
SELECT 列名, UPPER(列名) AS 别名
FROM 表名
WHERE 条件;

student 表包含 id(学号)、name(姓名)。查询姓名为 'L' 的学生,展示学号、姓名及大写姓名(upper_name):> student 表包含 id(学号)、name(姓名)。查询姓名为 'L' 的学生,展示学号、姓名及大写姓名(upper_name):

sql 复制代码
SELECT id, name, UPPER(name) AS upper_name
FROM student
WHERE name = 'L';
17、聚合函数 COUNT/SUM/AVG/MAX/MIN

聚合函数用于对数据集进行汇总计算,可执行计数、求和、平均值、最大值和最小值等操作,常与 GROUP BY 配合使用以对分组后的数据做汇总分析。

sql 复制代码
SELECT
  COUNT(列名) AS 计数,
  SUM(数值列名) AS 总和,
  AVG(数值列名) AS 平均值,
  MAX(列名) AS 最大值,
  MIN(列名) AS 最小值
FROM 表名
[WHERE 条件]
[GROUP BY 分组字段];

常见聚合函数****常见聚合函数

  • COUNT:计算指定列的行数或非空值数量。
  • SUM:计算数值列之和。
  • AVG:计算数值列的平均值。
  • MAX:求最大值。
  • MIN:求最小值。

student 表包含 id、name、class_id、score。查询全体学生的总成绩(total_score)、平均成绩(avg_score)、最高成绩(max_score)和最低成绩(min_score):

sql 复制代码
SELECT
  SUM(score)  AS total_score,
  AVG(score)  AS avg_score,
  MAX(score)  AS max_score,
  MIN(score)  AS min_score
FROM student;
34、数学函数 ROUND/CEIL/FLOOR/ABS/MOD/POWER

常用数学函数:ROUND 四舍五入、CEIL 向上取整、FLOOR 向下取整、ABS 绝对值、MOD 取余、POWER 幂运算。

sql 复制代码
SELECT ROUND(数值, 小数位数) AS 四舍五入结果,
       CEIL(数值) AS 向上取整结果,
       FLOOR(数值) AS 向下取整结果,
       ABS(数值) AS 绝对值结果,
       MOD(被除数, 除数) AS 取余结果,
       POWER(底数, 指数) AS 幂运算结果
FROM 表名;

student 表包含 name、score。查询学生姓名和成绩四舍五入保留 1 位小数的结果:

sql 复制代码
SELECT name, ROUND(score, 1) AS score_round
FROM student;
35、日期计算与格式化 DATE_FORMAT/DATEDIFF

常用日期函数:NOW() 当前时间、CURDATE() 当前日期、DATE_FORMAT() 格式化、DATEDIFF() 相差天数、DATE_ADD() / DATE_SUB() 日期加减、YEAR() / MONTH() / DAY() 取年月日。

sql 复制代码
-- 获取当前日期和时间
SELECT NOW();
SELECT CURDATE();

-- 格式化日期
SELECT DATE_FORMAT(日期, '%Y-%m-%d') FROM 表名;

-- 计算两个日期相差天数
SELECT DATEDIFF(日期1, 日期2);

-- 日期加减
SELECT DATE_ADD(日期, INTERVAL 偏移量 DAY) FROM 表名;
SELECT DATE_SUB(日期, INTERVAL 偏移量 DAY) FROM 表名;

-- 提取日期部分
SELECT YEAR(日期), MONTH(日期), DAY(日期) FROM 表名;

student 表包含 name、birthday。查询学生姓名、出生年份,以及出生日期距离当前日期的天数:

sql 复制代码
SELECT
  name,
  YEAR(birthday) AS birth_year,
  DATEDIFF(CURDATE(), birthday) AS days_from_birth
FROM student;
36、空值处理 IFNULL / COALESCE / NULLIF

IFNULL(值, 默认值) 在值为 NULL 时返回默认值;COALESCE(值1, 值2, ...) 返回第一个非 NULL 的值;NULLIF(值1, 值2) 两值相等时返回 NULL,否则返回值1。

sql 复制代码
-- 通用语法模板:空值处理函数
SELECT
    IFNULL(列, 默认值) AS 别名,          -- 列值为 NULL 时返回默认值
    COALESCE(列1, 列2, ..., 默认值) AS 别名, -- 返回第一个非 NULL 的值
    NULLIF(值1, 值2) AS 别名            -- 两值相等时返回 NULL,否则返回值1
FROM 表名
[WHERE 条件];

student 表包含 name、score。查询学生姓名和成绩,成绩为空时显示 0:

sql 复制代码
SELECT name, IFNULL(score, 0) AS score
FROM student;
37、类型转换 CAST / CONVERT

CAST(值 AS 类型) 与 CONVERT(值, 类型) 用于显式转换数据类型,常见类型包括 CHAR、SIGNED、DECIMAL、DATE、DATETIME。

sql 复制代码
-- 通用语法模板:显式类型转换
CAST(值 AS 类型)
CONVERT(值, 类型)

student 表包含 id、score。把学号转成字符串,成绩转成整数:

sql 复制代码
SELECT
  CAST(id AS CHAR)      AS id_str,
  CAST(score AS SIGNED) AS score_int
FROM student;

五、分组聚合 GROUP BY/HAVING

18、单字段分组 GROUP BY

GROUP BY 用于对数据进行分类,并对每个分类做聚合计算。
student 表包含 id、name、class_id、score。统计每个班级(class_id)的平均成绩(avg_score):

sql 复制代码
SELECT 分组字段, 聚合函数(列名) AS 别名
FROM 表名
GROUP BY 分组字段;
sql 复制代码
SELECT class_id, AVG(score) AS avg_score
FROM student
GROUP BY class_id;
19、多字段分组 GROUP BY

多字段分组按字段组合分类:GROUP BY 字段1, 字段2。

sql 复制代码
SELECT 分组字段1, 分组字段2, 聚合函数(列名) AS 别名
FROM 表名
[WHERE 条件]
GROUP BY 分组字段1, 分组字段2
[HAVING 分组后条件]
[ORDER BY 排序字段 [ASC|DESC]];

student 表包含 id、name、class_id、exam_num、score。统计每个班级、每场考试的学生人数(total_num):

sql 复制代码
SELECT class_id, exam_num, COUNT(*) AS total_num
FROM student
GROUP BY class_id, exam_num;
20、HAVING 子句

HAVING 用于在分组聚合后对结果进行过滤。与 WHERE 的区别:WHERE 在分组前过滤行,HAVING 在分组后过滤分组。

sql 复制代码
SELECT 分组字段, 聚合函数(列名) AS 别名
FROM 表名
GROUP BY 分组字段
HAVING 聚合函数(列名) 条件;

student 表包含 id、name、class_id、score。查询总成绩超过 150 分的班级编号(class_id)和总成绩(total_score):> student 表包含 id、name、class_id、score。查询总成绩超过 150 分的班级编号(class_id)和总成绩(total_score):

sql 复制代码
SELECT class_id, SUM(score) AS total_score
FROM student
GROUP BY class_id
HAVING SUM(score) > 150;
51、GROUP_CONCAT 合并字符串

GROUP_CONCAT(列 SEPARATOR '分隔符') 把同一分组内的多行值拼成一个字符串,常用于「一个班级所有学生姓名」这类展示。

sql 复制代码
SELECT 分组字段, GROUP_CONCAT(列名 [ORDER BY 排序字段 [ASC|DESC]] SEPARATOR '分隔符') [AS 别名]
FROM 表名
[WHERE 条件]
GROUP BY 分组字段;

student 表包含 name、class_id。查询每个班级的学生姓名,用逗号拼接:

sql 复制代码
SELECT class_id, GROUP_CONCAT(name SEPARATOR ',') AS names
FROM student
GROUP BY class_id;
52、WITH ROLLUP 小计与总计 GROUP BY

GROUP BY ... WITH ROLLUP 在分组结果后追加小计行和总计行,分组列为 NULL 的行即为汇总行。

sql 复制代码
SELECT 分组字段, 聚合函数(列名)
FROM 表名
[WHERE 条件]
GROUP BY 分组字段 WITH ROLLUP
[HAVING 聚合条件]
[ORDER BY 排序字段 [ASC|DESC]]
[LIMIT 偏移量, 条数];

student 表包含 class_id、score。统计每个班级总成绩并追加总计行:

sql 复制代码
SELECT class_id, SUM(score) AS total
FROM student
GROUP BY class_id WITH ROLLUP;

六、关联查询 JOIN

21、CROSS JOIN

CROSS JOIN 是一种最简单的关联查询,不需要任何匹配条件,直接把左表的每一行与右表的每一行组合,返回笛卡尔积,结果行数 = 左表行数 × 右表行数。

sql 复制代码
SELECT 列名
FROM 表名1 [别名1]
CROSS JOIN 表名2 [别名2];

student 表包含 id、name、age、class_id;class 表包含 id、name。将两表所有行组合,返回学生姓名、学生年龄、班级编号和班级名称:

sql 复制代码
SELECT
  s.name      AS student_name,
  s.age       AS student_age,
  s.class_id  AS class_id,
  c.name      AS class_name
FROM student s
CROSS JOIN class c;
22、INNER JOIN

INNER JOIN 根据两个表之间的关联条件,只返回两边都能匹配的行,即交集。

sql 复制代码
SELECT 列
FROM 表名1 [AS] 别名1
INNER JOIN 表名2 [AS] 别名2
    ON 别名1.关联列 = 别名2.关联列
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组条件]
[ORDER BY 排序字段 [ASC|DESC]];

student 表包含 id、name、age、class_id;class 表包含 id、name、level。按班级编号匹配,返回学生姓名、年龄、班级编号、班级名称和班级级别:

sql 复制代码
SELECT
  s.name      AS student_name,
  s.age       AS student_age,
  s.class_id  AS class_id,
  c.name      AS class_name,
  c.level     AS class_level
FROM student s
JOIN class c ON s.class_id = c.id;
31、LEFT JOIN / RIGHT JOIN / FULL JOIN

LEFT JOIN 以左表为主,保留左表全部行,右表无匹配时补 NULL;RIGHT JOIN 以右表为主;FULL JOIN 两表全部保留,任一侧无匹配补 NULL(MySQL 不直接支持,可用 LEFT JOIN UNION RIGHT JOIN 模拟)。

sql 复制代码
SELECT 列
FROM 表名 别名
LEFT JOIN 表名 别名 ON 别名.关联列 = 别名.关联列
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组条件]
[ORDER BY 排序字段 [ASC|DESC]]
[LIMIT 偏移量, 条数];

student 表包含 id、name、class_id;class 表包含 id、name。查询所有学生及其班级名称,没有班级的学生班级名称显示为空:

sql 复制代码
SELECT s.name AS student_name, c.name AS class_name
FROM student s
LEFT JOIN class c ON s.class_id = c.id;
32、自连接 SELF JOIN

自连接把同一张表当成两张表关联,必须起两个别名,常用于查找上下级、同班同学、比某人分数高的人等关系。

sql 复制代码
SELECT a.列, b.列
FROM 表 a
JOIN 表 b ON a.关联列 = b.关联列
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组条件]
[ORDER BY 排序字段 [ASC|DESC]]
[LIMIT 偏移量, 条数];

student 表包含 id、name、class_id、score。查询与「张三」同班的其他学生姓名:

sql 复制代码
SELECT b.name
FROM student a
JOIN student b ON a.class_id = b.class_id
WHERE a.name = '张三' AND b.name <> '张三';
33、多表关联 JOIN

三张及以上表关联时,继续用 JOIN ... ON ... 依次拼接,每增加一张表就补充一个关联条件,避免因漏条件导致笛卡尔积。

sql 复制代码
SELECT 列
FROM 表1 别名1
JOIN 表2 别名2 ON 别名1.关联列 = 别名2.关联列
JOIN 表3 别名3 ON 别名2.关联列 = 别名3.关联列
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组筛选条件]
[ORDER BY 排序字段 [ASC|DESC]]
[LIMIT 偏移量, 条数];

student(id、name、class_id)、class(id、name)、teacher(id、name、class_id)。查询学生姓名、班级名称和班主任姓名:

sql 复制代码
SELECT
  s.name AS student_name,
  c.name AS class_name,
  t.name AS teacher_name
FROM student s
JOIN class c ON s.class_id = c.id
JOIN teacher t ON t.class_id = c.id;

七、子查询 IN/EXISTS

23、子查询

子查询是嵌套在另一条 SQL 里的查询,用 () 包裹,先执行内层查询,再执行外层查询。

1、标量子查询:返回一行一列 → 配合 =、>、< 等比较

sql 复制代码
SELECT 列 FROM 表 WHERE 列 = (SELECT 单值 FROM 表 WHERE 条件);

2、列子查询:返回一列多行 → 配合 IN、NOT IN、ANY、ALL

sql 复制代码
SELECT 列 FROM 表 WHERE 列 IN (SELECT 列 FROM 表 WHERE 条件);

3、EXISTS:只判断是否存在匹配行,内层通常写 SELECT 1

sql 复制代码
SELECT 列
FROM 表 s
WHERE EXISTS (
  SELECT 1 FROM 表 t WHERE t.列 = s.列
);

4、表子查询:返回多行多列 → 放在 FROM 后作为临时表,必须起别名

sql 复制代码
SELECT 列
FROM (SELECT 列 FROM 表 WHERE 条件) AS t
WHERE 条件;

标量子查询速记

  • 内层只返回一行一列时,外层才能用 =、>、<、>=、<=、!= 直接比较。
  • 常见单值来源:AVG()、SUM()、MAX()、MIN()、COUNT(),或 LIMIT 1。
  • 内层返回多行会报错,此时应改用 IN 或加 LIMIT 1。

一句话要点

  • 子查询必须加括号,FROM 里的子查询必须起别名。
  • 标量子查询只能返回一行一列,否则报错。
  • 能用 JOIN 表达的优先用 JOIN,性能更好。
  • 嵌套不宜过深,否则可读性和性能都会下降。

student 表包含 id、name、age、score、class_id;class 表包含 id、name。使用子查询找出有对应班级的学生,返回姓名、分数和班级编号:

sql 复制代码
SELECT name, score, class_id
FROM student
WHERE class_id IN (SELECT DISTINCT id FROM class);
24、EXISTS、NOT EXISTS

EXISTS 用于检查主查询结果中是否存在满足条件的记录,返回布尔值;NOT EXISTS 用于查找不满足存在条件的记录。
student 表包含 id、name、age、score、class_id;class 表包含 id、name。使用 NOT EXISTS 找出不存在对应班级的学生,返回姓名、年龄和班级编号:

sql 复制代码
SELECT name, age, class_id
FROM student
WHERE NOT EXISTS (
  SELECT 1 FROM class WHERE student.class_id = class.id
);
53、ANY / ALL

ANY 表示与子查询结果中任意一个值比较成立即可;ALL 表示必须与子查询结果中所有值比较都成立。> ANY 等价于大于最小值,> ALL 等价于大于最大值。

sql 复制代码
WHERE 列 > ANY (SELECT 列 FROM 表 WHERE 条件)
WHERE 列 > ALL (SELECT 列 FROM 表 WHERE 条件)

student 表包含 name、score。查询成绩高于「1 班任意一个学生」的学生:

sql 复制代码
SELECT name, score
FROM student
WHERE score > ANY (
  SELECT score FROM student WHERE class_id = 1
);

八、组合查询

25、UNION 和 UNION ALL

UNION 合并多个查询结果集并去重;UNION ALL 合并结果集但不去重。上下查询的列数、顺序和类型应保持一致。
student 与 student_new 字段均为 id、name、age、score、class_id。查询两表所有学生的 name、age、score、class_id,并保留重复记录:

sql 复制代码
SELECT name, age, score, class_id FROM student
UNION ALL
SELECT name, age, score, class_id FROM student_new;
54、INTERSECT / EXCEPT

INTERSECT 取两个结果集的交集(去重);EXCEPT(Oracle 中为 MINUS)取差集,即在前一个结果中但不在后一个结果中的行。MySQL 8.0 之前需用 JOIN 或 IN 模拟。

sql 复制代码
SELECT ... INTERSECT SELECT ...;
SELECT ... EXCEPT SELECT ...;

查询既在 student 又在 student_new 中出现的姓名:

sql 复制代码
SELECT name FROM student
INTERSECT
SELECT name FROM student_new;

九、开窗函数

26、SUM OVER

开窗函数是一种强大的查询工具,可以在保留原始行明细的同时进行分组计算。聚合函数(如 SUM、AVG、COUNT)均可与 OVER 结合使用。

sql 复制代码
SUM(计算字段名) OVER (PARTITION BY 分组字段名)

student 表包含 id、name、age、class_id、score、exam_num。查询每个学生的详细信息,并计算每个班级的平均分:

sql 复制代码
SELECT
  id, name, age, class_id, score, exam_num,
  AVG(score) OVER (PARTITION BY class_id) AS class_avg_score
FROM student;
27、SUM OVER ORDER BY

在分组基础上按顺序累加,实现同组数据的累计求和。

sql 复制代码
SUM(计算字段名) OVER (
  PARTITION BY 分组字段名            -- 可选:按这些字段分组
  ORDER BY 排序字段 [ASC|DESC]       -- 可选:组内排序方式
  [ROWS BETWEEN 上边界 AND 下边界]   -- 可选:窗口帧范围
)

student 表包含 id、name、age、score、class_id。按分数升序,累计计算每个班级的学生总分(class_sum_score):

sql 复制代码
SELECT
  id, name, age, score, class_id,
  SUM(score) OVER (
    PARTITION BY class_id
    ORDER BY score ASC
  ) AS class_sum_score
FROM student;
28、RANK

RANK 用于对结果集排名;存在并列时保留相同名次,并跳过后续名次(如 1、1、3)。

sql 复制代码
RANK() OVER (
  PARTITION BY 列名1, 列名2, ...   -- 可选,分组列
  ORDER BY 列名3 [ASC|DESC], ...    -- 排序列及排序方式
) AS rank_column

student 表包含 id、name、age、score、class_id。按分数降序计算每个班级内学生的排名(ranking):

sql 复制代码
SELECT
  id, name, age, score, class_id,
  RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS ranking
FROM student;
29、ROW_NUMBER

ROW_NUMBER 为结果集中每一行分配唯一连续的序号,不会跳号(如 1、2、3)。

sql 复制代码
ROW_NUMBER() OVER (
  PARTITION BY column1, column2, ...   -- 可选,分组列
  ORDER BY column3 [ASC|DESC], ...      -- 排序列及排序方式
) AS row_number_column

student 表包含 id、name、age、score、class_id。按分数降序给每个班级内的学生分配编号(row_number):

sql 复制代码
SELECT
  id, name, age, score, class_id,
  ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS row_number
FROM student;
30、LAG / LEAD

LAG / LEAD 用于获取当前行之前 / 之后某一行的值,常用于相邻行比较或时间序列分析。

sql 复制代码
LAG(column_name, offset, default_value) OVER (
  PARTITION BY partition_column
  ORDER BY sort_column
);

LEAD(column_name, offset, default_value) OVER (
  PARTITION BY partition_column
  ORDER BY sort_column
);

student 表包含 id、name、age、score、class_id。按分数降序,获取每个班级内当前学生的上一名学生姓名(prev_name)和下一名学生姓名(next_name):

sql 复制代码
SELECT
  id,
  name,
  age,
  score,
  class_id,
  LAG(name) OVER (PARTITION BY class_id ORDER BY score DESC)  AS prev_name,
  LEAD(name) OVER (PARTITION BY class_id ORDER BY score DESC) AS next_name
FROM student;
49、DENSE_RANK / NTILE / FIRST_VALUE

DENSE_RANK() 并列同名次且不跳号(1、1、2);NTILE(n) 把结果均分成 n 桶并编号;FIRST_VALUE(列) / LAST_VALUE(列) 取窗口内首行 / 末行的值。

sql 复制代码
DENSE_RANK() OVER (PARTITION BY 分组列 ORDER BY 排序列 DESC)
NTILE(n) OVER (ORDER BY 排序列)
FIRST_VALUE(列) OVER (PARTITION BY 分组列 ORDER BY 排序列)

student 表包含 id、name、score、class_id。按班级给成绩排名,并列时不跳号:

sql 复制代码
SELECT
  id, name, score, class_id,
  DENSE_RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS dr
FROM student;
50、窗口帧 ROWS BETWEEN

窗口帧用于限定「当前行参与计算的范围」:ROWS BETWEEN n PRECEDING AND m FOLLOWING 表示前 n 行到后 m 行;UNBOUNDED PRECEDING 表示从组首开始。

sql 复制代码
聚合函数(列) OVER (
  PARTITION BY 分组列
  ORDER BY 排序列
  ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
)

student 表包含 id、name、score、class_id。计算每个学生与同班前一名的分数之和:

sql 复制代码
SELECT
  id, name, score, class_id,
  SUM(score) OVER (
    PARTITION BY class_id
    ORDER BY score
    ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
  ) AS sum_two
FROM student;

十、数据写入与表结构

39、数据插入 INSERT

INSERT INTO 表 (列...) VALUES (值...) 插入一行;一次写多组 VALUES 可批量插入;INSERT INTO 表 SELECT ... 可把查询结果直接写入表。

sql 复制代码
INSERT INTO 表 (列1, 列2) VALUES (值1, 值2);

INSERT INTO 表 (列1, 列2)
VALUES (值1, 值2), (值3, 值4);

INSERT INTO 表 (列1, 列2)
SELECT 列1, 列2 FROM 另一张表;

向 student 表插入一条学生记录:

sql 复制代码
INSERT INTO student (id, name, age, score, class_id)
VALUES (101, '张三', 20, 88.5, 1);
40、数据更新 UPDATE

UPDATE 表 SET 列 = 值 WHERE 条件 修改满足条件的行。不写 WHERE 会更新全表 ,务必先用 SELECT 验证条件。

sql 复制代码
UPDATE 表
SET 列1 = 值1, 列2 = 值2
WHERE 条件;

把 student 表中姓名为「张三」的学生成绩改为 95:

sql 复制代码
UPDATE student
SET score = 95
WHERE name = '张三';
41、数据删除 DELETE / TRUNCATE

DELETE FROM 表 WHERE 条件 删除满足条件的行,可回滚、可带条件;TRUNCATE TABLE 表 清空整表,速度快但不能回滚、不能带条件。

sql 复制代码
DELETE FROM 表 WHERE 条件;
TRUNCATE TABLE 表;

删除 student 表中成绩低于 60 的学生:

sql 复制代码
DELETE FROM student WHERE score < 60;
42、建表 CREATE TABLE 与常用数据类型

建表时需指定列名、数据类型和约束。常用类型:INT 整数、DECIMAL(m,n) 定点小数、VARCHAR(n) 变长字符串、CHAR(n) 定长字符串、DATE 日期、DATETIME 日期时间、TEXT 长文本。

sql 复制代码
CREATE TABLE 表名 (
  列名 数据类型 [约束],
  ...
);

创建学生表 student:

sql 复制代码
CREATE TABLE student (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  age INT,
  score DECIMAL(5,2) DEFAULT 0,
  class_id INT,
  birthday DATE
);
43、修改表结构 ALTER TABLE

ALTER TABLE 用于增删改列、改类型、加约束:ADD COLUMN 加列、DROP COLUMN 删列、MODIFY COLUMN 改类型、RENAME TO 改表名。

sql 复制代码
ALTER TABLE 表 ADD COLUMN 列 类型;
ALTER TABLE 表 DROP COLUMN 列;
ALTER TABLE 表 MODIFY COLUMN 列 新类型;
ALTER TABLE 表 RENAME TO 新表名;

给 student 表增加一个性别列 gender:

sql 复制代码
ALTER TABLE student ADD COLUMN gender VARCHAR(10);
44、约束 PRIMARY KEY / UNIQUE / NOT NULL / DEFAULT / FOREIGN KEY

约束用于保证数据完整性:PRIMARY KEY 主键唯一且非空、UNIQUE 唯一、NOT NULL 非空、DEFAULT 默认值、FOREIGN KEY 外键关联另一张表、CHECK 检查条件。

sql 复制代码
列名 类型 PRIMARY KEY
列名 类型 UNIQUE
列名 类型 NOT NULL DEFAULT 默认值
FOREIGN KEY (列) REFERENCES 另一张表(列)

创建班级表 class,并给学生表加外键关联班级:

sql 复制代码
CREATE TABLE class (
  id INT PRIMARY KEY,
  name VARCHAR(50) NOT NULL
);

ALTER TABLE student
  ADD CONSTRAINT fk_student_class
  FOREIGN KEY (class_id) REFERENCES class(id);
45、索引 CREATE INDEX

索引用于加速查询,代价是占用空间、降低写入速度。CREATE INDEX 建普通索引,CREATE UNIQUE INDEX 建唯一索引,DROP INDEX 删除索引。

sql 复制代码
CREATE INDEX 索引名 ON 表(列);
CREATE UNIQUE INDEX 索引名 ON 表(列);
DROP INDEX 索引名 ON 表;

给 student 表的 name 列建索引:

sql 复制代码
CREATE INDEX idx_student_name ON student(name);
46、视图 CREATE VIEW

视图是把一条查询保存成「虚拟表」,查询视图就像查询普通表,可简化复杂 SQL、控制字段暴露;视图本身不存数据,底层数据变化会同步体现。

sql 复制代码
CREATE VIEW 视图名 AS SELECT ...;
DROP VIEW 视图名;

创建「班级平均分」视图:

sql 复制代码
CREATE VIEW v_class_avg AS
SELECT class_id, AVG(score) AS avg_score
FROM student
GROUP BY class_id;

SELECT * FROM v_class_avg WHERE avg_score > 80;
47、事务 BEGIN / COMMIT / ROLLBACK

事务把多条语句打包成一个整体,要么全部成功、要么全部回滚。BEGIN 开启、COMMIT 提交、ROLLBACK 回滚,配合 InnoDB 引擎使用。

sql 复制代码
BEGIN;
  SQL 语句...;
COMMIT;   -- 或 ROLLBACK;

转账场景:A 减 100、B 加 100,任一步失败则整体回滚:

sql 复制代码
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;
48、常用 DCL 与权限管理

CREATE USER 建用户、GRANT 授权、REVOKE 回收权限、DROP USER 删除用户。生产环境应遵循最小权限原则。

sql 复制代码
CREATE USER '用户名'@'主机' IDENTIFIED BY '密码';
GRANT 权限 ON 库.表 TO '用户名'@'主机';
REVOKE 权限 ON 库.表 FROM '用户名'@'主机';

创建一个只读用户并授予 student 表的查询权限:

sql 复制代码
CREATE USER 'reader'@'%' IDENTIFIED BY '123456';
GRANT SELECT ON demo.student TO 'reader'@'%';

十一、进阶查询

55、递归查询 WITH RECURSIVE

递归 CTE 用于处理树形结构(部门层级、评论盖楼)。结构为「初始查询 UNION ALL 递归查询」,递归部分引用自身,必须有终止条件。

sql 复制代码
WITH RECURSIVE cte AS (
  SELECT ... -- 初始行
  UNION ALL
  SELECT ... FROM 表 JOIN cte ON ... -- 递归行
)
SELECT * FROM cte;

部门表 dept(id、name、parent_id)。查询 id = 1 部门及其所有下级部门:

sql 复制代码
WITH RECURSIVE dept_tree AS (
  SELECT id, name, parent_id FROM dept WHERE id = 1
  UNION ALL
  SELECT d.id, d.name, d.parent_id
  FROM dept d
  JOIN dept_tree t ON d.parent_id = t.id
)
SELECT * FROM dept_tree;
56、公共表表达式 CTE(WITH)

WITH 名称 AS (查询) 把一段查询命名成临时结果集,可被主查询多次引用,让嵌套子查询变扁平、可读性更好;多个 CTE 用逗号分隔。

sql 复制代码
WITH cte1 AS (SELECT ...),
     cte2 AS (SELECT ...)
SELECT * FROM cte1 JOIN cte2 ON ...;

student 表包含 class_id、score。先算班级平均分,再筛出平均分大于 80 的班级:

sql 复制代码
WITH class_avg AS (
  SELECT class_id, AVG(score) AS avg_score
  FROM student
  GROUP BY class_id
)
SELECT * FROM class_avg WHERE avg_score > 80;
57、行转列与列转行

行转列:用 CASE WHEN 或 IF 配合聚合函数,把「行上的分类值」变成「列」;列转行:用 UNION ALL 把多个列拆成多行。

sql 复制代码
SUM(CASE WHEN 分类列 = '值' THEN 数值列 ELSE 0 END) AS 新列名

score_log 表包含 name、subject、score。把语文、数学、英语三行转成三列:

sql 复制代码
SELECT
  name,
  SUM(CASE WHEN subject = '语文' THEN score ELSE 0 END) AS chinese,
  SUM(CASE WHEN subject = '数学' THEN score ELSE 0 END) AS math,
  SUM(CASE WHEN subject = '英语' THEN score ELSE 0 END) AS english
FROM score_log
GROUP BY name;
58、排名取 Top N 的三种写法

取每组前 N 名是高频面试题,常见三种写法:开窗函数 ROW_NUMBER、关联子查询计数、GROUP_CONCAT + SUBSTRING_INDEX。推荐开窗函数,语义清晰、性能可控。

sql 复制代码
SELECT * FROM (
  SELECT 列,
         ROW_NUMBER() OVER (PARTITION BY 分组列 ORDER BY 排序列 DESC) AS rn
  FROM 表
) t
WHERE rn <= N;

student 表包含 name、score、class_id。取每个班级成绩前 2 名:

sql 复制代码
SELECT * FROM (
  SELECT
    name, score, class_id,
    ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn
  FROM student
) t
WHERE rn <= 2;
59、慢查询与执行计划 EXPLAIN

EXPLAIN 查看 SQL 的执行计划,重点关注 type(访问类型,ALL 全表扫描最差,ref/range/index 较好)、key(实际使用的索引)、rows(预估扫描行数)、Extra(是否出现 Using filesort、Using temporary)。

sql 复制代码
EXPLAIN SELECT ...;

查看按姓名查询学生时的执行计划:

sql 复制代码
EXPLAIN SELECT * FROM student WHERE name = '张三';
60、SQL 书写规范与性能建议

好习惯比技巧更重要:列名写全、不用 SELECT *;条件列建索引;避免在索引列上做函数运算;LIKE 不以 % 开头;批量操作用一条语句代替循环;大表分页用「延迟关联」或游标。

sql 复制代码
-- 不推荐:对索引列做函数运算导致索引失效
WHERE DATE(create_time) = '2023-08-01'

-- 推荐:范围查询走索引
WHERE create_time >= '2023-08-01' AND create_time < '2023-08-02'

大表分页优化:先用索引列取出主键,再回表取数据:

sql 复制代码
SELECT s.*
FROM student s
JOIN (
  SELECT id FROM student ORDER BY id LIMIT 100000, 10
) t ON s.id = t.id;

速记总览:一句话 + 语法模板

按上文分类顺序整理,每条只保留「记忆点」和「可套用的语法骨架」,复习时先看记忆点,再默写模板。

一、基础查询

序号 记忆点 语法模板
1 全表查询:星号代表所有列 SELECT * FROM 表名;
2 选择查询:只取想要的列 SELECT 列1, 列2 FROM 表名;
3 别名 AS:给结果列换个好懂的名字 SELECT 列 AS '别名' FROM 表名;
4 常量与运算:查询时可直接计算 SELECT 列, 列 * 2 AS 新列 FROM 表名;

二、条件查询

序号 记忆点 语法模板
5 WHERE:按行筛选,只留满足条件的行 SELECT 列 FROM 表名 WHERE 条件;
6 比较运算符:> < >= <= !=,范围用 BETWEEN A AND B WHERE 列 != 值 / WHERE 列 BETWEEN A AND B
7 空值判断:空值不能用 =,要用 IS NULL WHERE 列 IS NULL / WHERE 列 IS NOT NULL
8 模糊查询:% 任意多字符,_ 单个字符 WHERE 列 LIKE '%关键字%' / WHERE 列 NOT LIKE '%关键字%'
9 逻辑运算:AND 同时满足,OR 满足其一,NOT 取反 WHERE 条件1 AND 条件2 / WHERE 条件1 OR 条件2 / WHERE NOT 条件

三、结果处理

序号 记忆点 语法模板
10 去重:DISTINCT 作用于整行组合 SELECT DISTINCT 列1, 列2 FROM 表名;
11 排序:ASC 升序(默认)、DESC 降序,多字段依次比较 ORDER BY 列1 DESC, 列2 ASC
13 截断偏移:LIMIT m 取前 m 条;LIMIT n, m 跳过 n 条再取 m 条 LIMIT m / LIMIT n, m
14 条件分支:类似 if-else,从上往下匹配,命中即止 CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 ELSE 其他 END AS 新列
38 分页:ORDER BY + LIMIT,排序字段有重复值时追加唯一列兜底 ORDER BY 排序列, 唯一列 LIMIT 偏移量, 条数

四、函数

序号 记忆点 语法模板
15 时间函数:DATE 日期、DATETIME 日期时间、TIME 时间 SELECT DATE() AS 当前日期 FROM 表名;
16 字符串函数:大小写、长度、拼接、截取、替换、去空格 UPPER(str) / LOWER(str) / CONCAT(a, b) / SUBSTRING(str, pos, len) / REPLACE(str, 旧, 新) / TRIM(str)
17 聚合函数:多行压成一行,常配 GROUP BY SELECT COUNT(*), SUM(列), AVG(列), MAX(列), MIN(列) FROM 表名;
34 数学函数:四舍五入、取整、绝对值、取余、幂 ROUND(数值, n) / CEIL(x) / FLOOR(x) / ABS(x) / MOD(a, b) / POWER(a, b)
35 日期计算:格式化、相差天数、日期加减、取年月日 DATE_FORMAT(日期, '%Y-%m-%d') / DATEDIFF(d1, d2) / DATE_ADD(日期, INTERVAL n DAY) / YEAR(日期)
36 空值处理:IFNULL 给默认值、COALESCE 取第一个非空、NULLIF 相等转 NULL IFNULL(列, 默认值) / COALESCE(列1, 列2, 默认值) / NULLIF(列, 值)
37 类型转换:显式转字符串、整数、小数、日期 CAST(值 AS 类型) / CONVERT(值, 类型)

五、分组聚合

序号 记忆点 语法模板
18 单字段分组:每个分组出一行 SELECT 分组列, AVG(列) FROM 表名 GROUP BY 分组列;
19 多字段分组:按字段组合分组 SELECT 列1, 列2, COUNT(*) FROM 表名 GROUP BY 列1, 列2;
20 HAVING:分组之后再过滤;WHERE 在分组前,HAVING 在分组后 GROUP BY 列 HAVING 聚合条件
51 GROUP_CONCAT:把组内多行值拼成一个字符串 SELECT 分组列, GROUP_CONCAT(列 SEPARATOR ',') FROM 表名 GROUP BY 分组列;
52 WITH ROLLUP:分组结果后追加小计行和总计行 GROUP BY 分组列 WITH ROLLUP

六、关联查询

序号 记忆点 语法模板
21 CROSS JOIN:无条件笛卡尔积,行数 = 左表行数 × 右表行数 SELECT * FROM A CROSS JOIN B;
22 INNER JOIN:按关联条件取交集 SELECT 列 FROM A a JOIN B b ON a.关联列 = b.关联列;
31 LEFT / RIGHT / FULL JOIN:left 保左表、right 保右表、full 两边都保,缺的补 NULL FROM A a LEFT JOIN B b ON a.列 = b.列 / RIGHT JOIN / FULL JOIN
32 自连接:同一张表起两个别名互相关联 FROM 表 a JOIN 表 b ON a.列 = b.列 WHERE 条件
33 多表关联:每加一张表补一个 ON,漏条件会变笛卡尔积 FROM A a JOIN B b ON a.列 = b.列 JOIN C c ON b.列 = c.列

七、子查询

序号 记忆点 语法模板
23 子查询:括号包裹,先内后外;标量配 =,列子查询配 IN,表子查询放 FROM 且必须起别名 WHERE 列 = (SELECT 单值 ...) / WHERE 列 IN (SELECT 列 ...) / FROM (SELECT ...) AS t
24 EXISTS / NOT EXISTS:只判断有没有行,内层写 SELECT 1 WHERE EXISTS (SELECT 1 FROM B WHERE B.列 = A.列) / WHERE NOT EXISTS (...)
53 ANY / ALL:> ANY 大于最小值,> ALL 大于最大值 WHERE 列 > ANY (SELECT 列 FROM 表 WHERE 条件) / > ALL (...)

八、组合查询

序号 记忆点 语法模板
25 UNION 去重合并,UNION ALL 保留重复;上下查询列数、类型要一致 SELECT ... UNION ALL SELECT ...
54 INTERSECT 取交集,EXCEPT 取差集 SELECT ... INTERSECT SELECT ... / SELECT ... EXCEPT SELECT ...

九、开窗函数

序号 记忆点 语法模板
26 SUM OVER:分组内汇总,但保留每一行明细 聚合函数(列) OVER (PARTITION BY 分组列)
27 SUM OVER ORDER BY:组内按顺序累加,实现累计求和 SUM(列) OVER (PARTITION BY 分组列 ORDER BY 排序列 ASC)
28 RANK:并列同名次,后续名次跳号(1、1、3) RANK() OVER (PARTITION BY 分组列 ORDER BY 排序列 DESC) AS ranking
29 ROW_NUMBER:连续唯一编号,不跳号(1、2、3) ROW_NUMBER() OVER (PARTITION BY 分组列 ORDER BY 排序列 DESC) AS rn
30 LAG / LEAD:取上一行 / 下一行的值,可设偏移和默认值 LAG(列, offset, default) OVER (PARTITION BY 分组列 ORDER BY 排序列) / LEAD(...) OVER (...)
49 DENSE_RANK / NTILE / FIRST_VALUE:并列不跳号 / 均分 n 桶 / 取窗口首行 DENSE_RANK() OVER (...) / NTILE(n) OVER (...) / FIRST_VALUE(列) OVER (...)
50 窗口帧:限定当前行参与计算的范围 聚合函数(列) OVER (... ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)

十、数据写入与表结构

序号 记忆点 语法模板
39 INSERT:插入一行 / 批量插入 / 查询结果写入表 INSERT INTO 表 (列...) VALUES (值...);
40 UPDATE:改数据必须带 WHERE,否则全表更新 UPDATE 表 SET 列 = 值 WHERE 条件;
41 DELETE / TRUNCATE:DELETE 可带条件可回滚,TRUNCATE 清空整表不可回滚 DELETE FROM 表 WHERE 条件; / TRUNCATE TABLE 表;
42 CREATE TABLE:建表指定列名、类型、约束 CREATE TABLE 表 (列 类型 约束, ...);
43 ALTER TABLE:加列、删列、改类型、改表名 ALTER TABLE 表 ADD COLUMN 列 类型;
44 约束:主键唯一非空,外键关联他表 PRIMARY KEY / UNIQUE / NOT NULL DEFAULT 默认值 / FOREIGN KEY (列) REFERENCES 表(列)
45 索引:加速查询,代价是占空间、降写入 CREATE INDEX 索引名 ON 表(列);
46 视图:把查询保存为虚拟表,不存数据 CREATE VIEW 视图名 AS SELECT ...;
47 事务:BEGIN 开启、COMMIT 提交、ROLLBACK 回滚 BEGIN; ... COMMIT; / BEGIN; ... ROLLBACK;
48 DCL:建用户、授权、回收权限 CREATE USER / GRANT / REVOKE

十一、进阶查询

序号 记忆点 语法模板
55 递归 CTE:初始行 UNION ALL 递归行,必须有终止条件 WITH RECURSIVE cte AS (初始 UNION ALL 递归) SELECT * FROM cte;
56 CTE(WITH):给查询命名成临时结果集,可多处引用 WITH cte AS (SELECT ...) SELECT * FROM cte;
57 行转列:CASE WHEN + 聚合;列转行:UNION ALL SUM(CASE WHEN 分类列 = '值' THEN 数值列 ELSE 0 END) AS 列名
58 Top N:ROW_NUMBER 开窗最推荐,语义清晰 SELECT * FROM (SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM 表) t WHERE rn <= N;
59 EXPLAIN:看 type、key、rows、Extra 定位慢查询 EXPLAIN SELECT ...;
60 书写规范:列名写全、索引列不套函数、LIKE 不以 % 开头 WHERE create_time >= '...' AND create_time < '...'

十二、执行顺序速记

写 SQL 的顺序和数据库执行的顺序并不一致,记住下面这条链,很多「为什么这里不能写别名」的问题就通了:
#mermaid-svg-ad3pJIxCzgC3Rt8W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ad3pJIxCzgC3Rt8W .error-icon{fill:#552222;}#mermaid-svg-ad3pJIxCzgC3Rt8W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ad3pJIxCzgC3Rt8W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .marker.cross{stroke:#333333;}#mermaid-svg-ad3pJIxCzgC3Rt8W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ad3pJIxCzgC3Rt8W p{margin:0;}#mermaid-svg-ad3pJIxCzgC3Rt8W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster-label text{fill:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster-label span{color:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster-label span p{background-color:transparent;}#mermaid-svg-ad3pJIxCzgC3Rt8W .label text,#mermaid-svg-ad3pJIxCzgC3Rt8W span{fill:#333;color:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .node rect,#mermaid-svg-ad3pJIxCzgC3Rt8W .node circle,#mermaid-svg-ad3pJIxCzgC3Rt8W .node ellipse,#mermaid-svg-ad3pJIxCzgC3Rt8W .node polygon,#mermaid-svg-ad3pJIxCzgC3Rt8W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .rough-node .label text,#mermaid-svg-ad3pJIxCzgC3Rt8W .node .label text,#mermaid-svg-ad3pJIxCzgC3Rt8W .image-shape .label,#mermaid-svg-ad3pJIxCzgC3Rt8W .icon-shape .label{text-anchor:middle;}#mermaid-svg-ad3pJIxCzgC3Rt8W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .rough-node .label,#mermaid-svg-ad3pJIxCzgC3Rt8W .node .label,#mermaid-svg-ad3pJIxCzgC3Rt8W .image-shape .label,#mermaid-svg-ad3pJIxCzgC3Rt8W .icon-shape .label{text-align:center;}#mermaid-svg-ad3pJIxCzgC3Rt8W .node.clickable{cursor:pointer;}#mermaid-svg-ad3pJIxCzgC3Rt8W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .arrowheadPath{fill:#333333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ad3pJIxCzgC3Rt8W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ad3pJIxCzgC3Rt8W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ad3pJIxCzgC3Rt8W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster text{fill:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W .cluster span{color:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ad3pJIxCzgC3Rt8W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ad3pJIxCzgC3Rt8W rect.text{fill:none;stroke-width:0;}#mermaid-svg-ad3pJIxCzgC3Rt8W .icon-shape,#mermaid-svg-ad3pJIxCzgC3Rt8W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ad3pJIxCzgC3Rt8W .icon-shape p,#mermaid-svg-ad3pJIxCzgC3Rt8W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ad3pJIxCzgC3Rt8W .icon-shape .label rect,#mermaid-svg-ad3pJIxCzgC3Rt8W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ad3pJIxCzgC3Rt8W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ad3pJIxCzgC3Rt8W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ad3pJIxCzgC3Rt8W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} FROM / JOIN 取表
WHERE 行过滤
GROUP BY 分组
HAVING 组过滤
SELECT 取列与计算
DISTINCT 去重
ORDER BY 排序
LIMIT 截断

一句话口诀:表 → 行 → 组 → 组筛 → 列 → 去重 → 排序 → 截断。

十三、易错点清单

  • 空值判断只能用 IS NULL / IS NOT NULL,写 = NULL 永远不成立。
  • WHERE 里不能直接使用聚合函数,聚合条件要放到 HAVING。
  • SELECT 中的非聚合列必须出现在 GROUP BY 里(标准 SQL 要求)。
  • LIMIT n, m 的 n 是「跳过的条数」,从 0 开始算,所以「从第 2 条开始」写 LIMIT 1, 3。
  • UNION 会自动去重并排序,有性能开销;确定无重复时用 UNION ALL。
  • 开窗函数不会减少行数,聚合函数不加 OVER 会把多行压成一行,两者别混用。
  • RANK 跳号、ROW_NUMBER 不跳号、DENSE_RANK 不跳号但并列同名次,按需求选。
  • UPDATE、DELETE 不带 WHERE 会作用于全表,执行前先 SELECT 验证条件。
  • FROM 后的子查询必须起别名,否则语法报错。
  • 多表关联漏写 ON 条件会退化成笛卡尔积,结果行数爆炸。
相关推荐
tongyiixiaohuang2 小时前
供应商主数据同步实战:从易快报到金蝶云星空的单一策略落地
java·网络·数据库
大侠归来3 小时前
常用 MySQL 分库分表方案详解
数据库·mysql
维克兜率天3 小时前
【维克】指数调整:哪些股票会被“强制买入“?
人工智能·python·算法
kingx33 小时前
Windows Server 2008 应急加固实战:7 项入侵痕迹排查与修复
数据库·windows·提权·应急响应·加固
罗西的思考3 小时前
【轻量化复刻大模型】线性注意力交互式演示
人工智能·算法·机器学习
xiaogai_gai3 小时前
采购业务退款单同步实战:从钉钉审批流到金蝶云星空付款单
java·数据库·钉钉
神也畏惧菠萝4 小时前
Day08 Python流程控制从入门到上瘾:if/match/while/for四件套,从B站登录一路打到猜数字游戏
python·算法·数据存储·for·while·if·ifelse
揽星河..4 小时前
【C语言】分支和循环
c语言·c++·算法