# 关联查询+批量插入:从y表和users表关联查询数据,再插回y表 INSERT INTO y (id, name) # 往y表的id、name字段插入数据 SELECT o.id, u.name # 取y表(别名o)的id、users表(别名u)的name FROM y o # 数据源1:y表,起别名o JOIN users u ON o.user_id = u.id; # 关联users表(别名u):y.user_id = users.id
3. INSERT SELECT 和普通 INSERT 的区别
1 2
INSERT INTO x VALUES (1, 'test', 20); # 手写值、一次一行(少量) INSERT INTO y SELECT*FROM users; # 来源是查询结果、可一次插 成千上万行、效率高
4. 和主键 / 唯一键冲突怎么办?
1. 冲突就失败(默认)
1
INSERT INTO x (id, name) SELECT id, name FROM users; # 如果id已存在,直接报错,整条失败
2. 忽略冲突行(不推荐滥用)
1
INSERT IGNORE INTO x (id, name) SELECT id, name FROM users; # 冲突的行被跳过、不报错、容易悄悄丢数据
3. 冲突就更新(常用)
1 2 3 4 5
# 有就更新,没有就插入: INSERT INTO x (id, name) SELECT id, name FROM users ON DUPLICATE KEY UPDATE name =VALUES(name);
4. 聚合函数
聚合函数可以把多行数据计算成一个结果,常与 GROUP BY 和 HAVING 配合使用,WHERE 负责聚合前过滤,HAVING 负责聚合后过滤。
1. 常见聚合函数一览
函数
作用
解释
COUNT()
计数
有多少行
SUM()
求和
数字加起来
AVG()
平均值
求平均
MAX()
最大值
找最大的
MIN()
最小值
找最小的
1. COUNT(计数、常用)
1 2 3 4
SELECTCOUNT(*) FROM users; # 统计users表一共有多少行数据(*会统计NULL)
SELECTCOUNT(列名) FROM users; # 不会统计NULL SELECTCOUNT(email) FROM users; # 只统计email不为NULL的行数
2. SUM(求和)
1 2
SELECTSUM(列名) FROM users; SELECTSUM(id) FROM users; # 把users表中所有的id加和
注意:列必须是数字、NULL 会被自动忽略。
3. AVG(平均值)
1 2
SELECTAVG(列名) FROM users; SELECTAVG(age) FROM users; # 统计users表中所有年龄的平均值
注意:NULL 不参与计算、返回值可能是小数。
4. MAX / MIN(最大最小)
1
SELECTMAX(age), MIN(age) FROM users; # 找出users表中最大和最小年龄
执行顺序:WHERE(先筛选行)→ GROUP BY(再分组)→ 聚合函数(每组算一次)→ HAVING(对分组后的结果筛选)→ ORDER BY(排序) → LIMIT(分页)。
2. 示例
1. 按某个字段分组统计数量
1 2 3 4
# 按age字段分组,统计每个年龄的人数 SELECT age, COUNT(*) AS cnt # 查age值,用COUNT(*)统计每组的行数,别名cnt FROM users # 查的是users表 GROUPBY age; # 按age字段分组(相同age的行是一组)
2. 分组 + WHERE(常见)
1 2 3 4 5
# 先找age=18的用户,再按id分组统计 SELECT id, COUNT(*) AS my_id # 查id,统计每组行数,别名my_id FROM users # 查的是users表 WHERE age =18 # 先找出age=18的行 GROUPBY id; # 再按id分组
3. 分组 + 多个聚合函数
1 2 3 4 5 6
SELECT class_id, # 按班级分组,展示班级ID COUNT(*) AS student_count, # 统计每个班级的学生总数(别名student_count) SUM(age) AS total_age, # 计算每个班级学生的年龄总和(别名total_age) MAX(age) AS max_age # 找出每个班级学生的最大年龄(别名max_age) FROM students # 学生表 GROUPBY class_id; # 按班级ID分组(同一班级的学生归为一组)
3. GROUP BY
非聚合列:比如 id、age(是直接从表中取的字段,不是聚合计算的结果)。
聚合列:比如 COUNT(*)(是对一组数据计算后的结果)。
SELECT 里出现的非聚合列,必须出现在 GROUP BY 中:SELECT 中出现的 非聚合列(直接取的学生 / 班级字段,如 class_id、gender),必须出现在 GROUP BY 里,只有 聚合列(COUNT/SUM/AVG 等计算结果),可以不出现在 GROUP BY 里。这句话可能不要好理解,说白了就是:只要 SELECT 里所有 “直接抄的字段”(非聚合列),在后面 GROUP BY 中出现就合法,不管聚合列有 1 个还是 10 个,都绝对安全,反之,哪怕只漏 1 个非聚合列,就会报错/出脏数据。
1. 错误示例
1 2 3 4 5 6 7
# 错误写法:SELECT里有class_id、gender(两个非聚合列),但GROUPBY只写了class_id SELECT class_id, # 非聚合列1 gender, # 非聚合列2(未出现在GROUPBY) COUNT(*) AS student_num # 聚合列(统计人数) FROM student GROUPBY class_id; # 仅按班级分组
原因:同一个 class_id 可能有多条 gender,MySQL 不知道该选哪一个。
2. 正确使用
1 2 3 4 5
# class_id是分组依据(非聚合列),出现在GROUPBY里;COUNT(*)是对每个班级的学生数做聚合计算,不需要GROUPBY SELECT class_id, # 非聚合列(出现在GROUPBY,作为分组依据) COUNT(*) AS student_num # 聚合列(无需在GROUPBY) FROM student GROUPBY class_id; # 分组依据:班级ID
HAVING 是 对「分组后的结果」进行筛选(过滤掉不满足条件的分组),搭配 GROUP BY 使用。
1 2 3 4 5 6 7 8 9 10 11
SELECT class_id, COUNT(*) AS student_num FROM student WHERE age >=18 # 分组前:只保留年龄≥18的学生 GROUPBY class_id;
SELECT class_id, COUNT(*) AS student_num FROM student GROUPBY class_id HAVINGCOUNT(*) >=3; # 分组后:只保留学生数≥3的班级
2. WHERE vs HAVING
对比
WHERE
HAVING
筛选时机
分组 之前 筛选行
分组 之后 筛选分组
作用对象
原始行(比如单个学生)
分组结果(比如整个班级)
是否依赖 GROUP BY
不需要(可单独用)
必须搭配 GROUP BY 使用
能否用聚合函数
不能(只能用原始字段,如 age < 18)
能(可以用聚合函数,如 COUNT(*) > 2)
3. 示例
1 2 3 4 5 6
SELECT class_id, COUNT(*) AS student_num FROM student GROUP BY class_id # 步骤1:按班级分组统计人数 HAVING COUNT(*) >= 2 # 步骤2:过滤出人数≥2的班级 ORDER BY student_num DESC, class_id ASC; # 步骤3:对过滤后的结果排序
1 2 3 4 5 6
SELECT class_id, gender, COUNT(*) AS student_num FROM student GROUP BY class_id, gender # 步骤1:按“班级+性别”分组统计 HAVING COUNT(*) >= 2 # 步骤2:过滤出人数≥2的分组 ORDER BY student_num DESC, class_id ASC; # 步骤3:对过滤后的结果排序