[TOC]
03:mysql之单表增删改查
今日工作任务路线图
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end一、工作场景
在电商运营工作中,单表的增删改查操作十分常见。运营人员为了丰富商品种类,会向商品信息表中新增商品记录,包括商品名称、价格、库存等信息,以满足市场需求。若某商品价格调整,运营人员会及时修改商品信息表中该商品的价格字段,保证数据准确性。当一款商品下架时,他们会从表中删除该商品的记录,避免无效数据干扰。同时,在分析销售数据时,运营人员会根据不同条件从表中查询特定商品的信息,比如筛选出销量高的商品,为后续的营销策略提供数据支持。
二、为什么学增删改查
学习 MySQL 单表的增删改查至关重要。在数据管理领域,这是最基础且核心的技能。在日常工作里,我们常常要与各种数据打交道,增删改查操作能让我们灵活处理数据。比如,电商平台需新增商品信息、删除滞销商品数据、修改商品价格、查询特定商品库存。掌握这些操作,能高效维护数据,确保数据实时性与准确性。同时,它们是学习更复杂数据库操作的基石,如多表连接、存储过程等。对于从事数据分析、软件开发等行业的人而言,熟练运用增删改查是必备技能,能提升工作效率与质量。
三、分组查询
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class A highlight;分组函数 group【格鲁普】 by
GROUP【格鲁普】 BY是 SQL 中非常重要的一个子句,它的主要作用是对查询结果进行分组统计
| 特性 | 说明 | 示例或关键点 |
|---|---|---|
| 核心功能 | 根据指定列将数据分成若干组,然后对每个组应用聚合函数进行统计。 | 计算每个部门的平均工资、每个产品的总销售额等。 |
| 基本语法 | SELECT【涩莱克特】 列A, 聚合函数(列B) FROM【弗乱】 表名 GROUP【格鲁普】 BY 列A;。 | 列A是分组依据。 |
| 常用聚合函数 | COUNT【kàn特】()(计数), SUM【桑à姆】()(求和), AVG()(平均值), MAX【mà 斯】()(最大值), MIN【敏ì】()(最小值)。 | 对每个分组进行计算。 |
| 与HAVING子句 | WHERE【威尔】 在分组前过滤行,HAVING【嗨夫营】 在分组后过滤组。 | HAVING【嗨夫营】 AVG(salary【晒了瑞】) > 5000(筛选出平均工资大于5000的组)。 |
| 多列分组 | GROUP【格鲁普】 BY 后面可接多个列,按这些列的组合进行分组。 | GROUP【格鲁普】 BY department【迪帕踢门特】, job_title。 |
| 重要注意事项 | SELECT【涩莱克特】 后面非聚合的列,必须出现在 GROUP【格鲁普】 BY 子句中。 | 这是最容易出错的地方。 |
💡 理解分组的过程
你可以把 GROUP【格鲁普】 BY 想象成一个高效的数据整理师。它的工作流程通常是:首先,根据 GROUP【格鲁普】 BY 后面指定的列,将表中所有数据行分成不同的“小组”,每个小组内的这些列的值完全相同。然后,数据库会对每一个这样的小组分别计算聚合函数(如求和、计数等)。最后,查询结果将为每个小组返回一行摘要信息,其中包含分组列的值和聚合函数计算的结果。
例如,有一个员工表,使用 SELECT【涩莱克特】 department【迪帕踢门特】, AVG(salary【晒了瑞】) FROM【弗乱】 employees【ing普洛伊s】 GROUP【格鲁普】 BY department【迪帕踢门特】; 可以计算出每个部门的平均工资。在这里,数据库会先按部门进行分组,然后对每个部门小组计算平均工资。
🛠️ 基本用法与示例
单列分组 这是最常见的情况,即根据一个字段进行分组。
-- 统计每个城市的用户数量SELECT【涩莱克特】 city, COUNT【kàn特】(*) AS user_count【kàn特】FROM【弗乱】 usersGROUP【格鲁普】 BY city;多列分组 当需要根据多个字段的组合进行更精细的分组时,可以使用多列分组。
-- 统计每个部门下每个职位的员工数量SELECT【涩莱克特】 department【迪帕踢门特】, job_title, COUNT【kàn特】(*) AS employee【ing普洛伊】_count【kàn特】FROM【弗乱】 employees【ing普洛伊s】GROUP【格鲁普】 BY department【迪帕踢门特】, job_title;这个查询会返回所有唯一的“部门-职位”组合及其对应的员工数量。
⚠️ 关键规则与常见错误
-
SELECT【涩莱克特】列表的非聚合列规则 这是使用
GROUP【格鲁普】 BY时最需要牢记的规则:出现在SELECT【涩莱克特】列表中的列,要么必须包含在GROUP【格鲁普】 BY子句中,要么必须被包含在聚合函数里。这是因为,对于每个分组来说,分组列的值是唯一的(比如“技术部”),但其他列可能有多条不同的记录(比如“技术部”里有很多不同的员工名)。数据库无法确定应该返回哪一条非分组列的值,因此强制要求通过聚合函数(如MAX【mà 斯】,AVG)将其汇总成一个值,或者将其本身作为分组依据。 -
WHERE【威尔】 与 HAVING【嗨夫营】 的区别
WHERE【威尔】:在 分组之前 过滤数据行。它不能包含聚合函数。HAVING【嗨夫营】:在 分组之后 过滤分组结果。它通常与聚合函数一起使用,用来筛选满足特定条件的组。
-- 正确示例:先过滤出2023年以后的订单,再按客户分组,最后筛选出总金额大于1000的客户SELECT【涩莱克特】 customer_id, SUM【桑à姆】(amount) AS total【偷ò头ō】_amountFROM【弗乱】 orders【欧朵s】WHERE【威尔】 order【欧朵】_date >= '2023-01-01' -- 分组前过滤行GROUP【格鲁普】 BY customer_idHAVING【嗨夫营】 SUM【桑à姆】(amount) > 1000; -- 分组后过滤组
🔍 高级用法与技巧
- 使用表达式或函数分组:可以根据表达式的结果进行分组,这在处理时间序列数据时特别有用。例如,可以按年份和月份分组统计订单。
- 使用ROLLUP生成小计和总计:
WITH ROLLUP选项可以在分组结果的基础上,生成层次性的小计和总计行。 - 处理NULL【nò】值:需要注意的是,如果分组列中包含
NULL【nò】值,所有NULL【nò】值会被归为同一组。
命令操作如下所示:
输出符合条件的shell 和 name
mysql> select【涩莱克特】 shell,name from【弗乱】 moershi.user where【威尔】shell in ("/bin/bash","/sbin/nologin【诺劳京(白话)】");+---------------+-----------------+| shell | name |+---------------+-----------------+| /bin/bash | root || /sbin/nologin | bin || /sbin/nologin | daemon || /sbin/nologin | adm |.................| /sbin/nologin | rpc || /sbin/nologin | rpcuser || /sbin/nologin | nfsnobody || /sbin/nologin | haproxy || /bin/bash | plj || /sbin/nologin | apache |+---------------+-----------------+22 rows in set【赛特】 (0.00 sec)统计每种解释器用户的个数 (按照shell表头值分组统计name表头值个数)
mysql> select【涩莱克特】 shell as 解释器 , count【kàn特】(name) as 总人数 from【弗乱】 moershi.user where【威尔】shell in ("/bin/bash","/sbin/nologin【诺劳京(白话)】") group【格鲁普】 by shell;+---------------+-----------+| 解释器 | 总人数 |+---------------+-----------+| /bin/bash | 2 || /sbin/nologin | 20 |+---------------+-----------+| 子句/部分 | 说明 |
|---|---|
SELECT | 指定要查询的列。 |
shell AS 解释器 | 查询 shell 字段,并将其别名设置为“解释器”。 |
COUNT(name) AS 总人数 | 使用 COUNT 聚合函数统计每个分组中 name 的数目,别名是“总人数”。 |
FROM moershi.user | 指定查询的表为 moershi 数据库下的 user 表。 |
WHERE shell IN (...) | 过滤条件,只统计使用/bin/bash或/sbin/nologin的用户。 |
GROUP BY shell | 根据 shell 字段的值进行分组,将使用相同Shell的用户归为一组进行统计。 |
统计每个部门的总人数 (按照部门表头分组统计name表头值的个数)
mysql> select【涩莱克特】 dept_id , count【kàn特】(name) from【弗乱】moershi.employees【ing普洛伊s】 group【格鲁普】 by dept_id ;+---------+-------------+| dept_id | count【kàn特】(name) |+---------+-------------+| 1 | 8 || 2 | 5 || 3 | 6 || 4 | 55 || 5 | 12 || 6 | 9 || 7 | 35 || 8 | 3 |+---------+-------------+8 rows in set【赛特】 (0.00 sec)四、对查询结果进行排序
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果</n>进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class B highlight;排序函数 order【欧朵】 by
| 特性 | 说明 | 示例或关键点 |
|---|---|---|
| 核心功能 | 根据指定的列对查询结果集进行排序。 | 让数据展示更清晰,便于分析和查看。 |
| 基本语法 | `SELECT【涩莱克特】 … FROM【弗乱】 … ORDER【欧朵】 BY column【科勒姆】_name [ASC【阿斯克】 | desc];` |
| 排序方式 | ASC【阿斯克】:升序(默认,可省略)。 desc:降序。 | 若不指定,默认为 ASC【阿斯克】。 |
| 多列排序 | 按多个字段排序,优先级从左到右。 | ORDER【欧朵】 BY column1【科勒姆】 desc, column2【科勒姆】 ASC【阿斯克】; |
| 排序键类型 | 字段名、字段在SELECT【涩莱克特】中的序号(从1开始)、字段别名。 | ORDER【欧朵】 BY 1(按第一列排序)。 |
| 与聚合函数 | 可使用聚合函数结果排序。 | ORDER【欧朵】 BY COUNT【kàn特】(*) desc; |
| NULL【nò】值处理 | NULL【nò】值可能集中出现在开头或末尾,具体取决于数据库系统。 | 需特别注意其默认位置。 |
💡 理解排序规则与语法
ORDER【欧朵】 BY 子句的核心作用是将查询结果按照特定顺序排列。其基本语法遵循以下结构:
SELECT【涩莱克特】 column1【科勒姆】, column2【科勒姆】, ...FROM【弗乱】 table【忒-部】_nameORDER【欧朵】 BY column1【科勒姆】 [ASC【阿斯克】|desc], column2【科勒姆】 [ASC【阿斯克】|desc], ...;这里,ASC【阿斯克】 表示升序(Ascending),desc 表示降序(Descending)。如果省略排序方式,数据库将默认按照升序(ASC【阿斯克】)排列结果 。
🛠️ 掌握多种排序方式
单列排序 这是最基础的排序方式,即根据单个字段的值进行排序。
-- 按产品名称升序排列(ASC【阿斯克】可省略)SELECT【涩莱克特】 prod_name FROM【弗乱】 products ORDER【欧朵】 BY prod_name ASC【阿斯克】;多列排序与优先级
当需要根据多个条件排序时,可以在 ORDER【欧朵】 BY 后列出多个列,它们具有明确的优先级:写在前面的字段优先级最高。数据库会先按第一个字段排序,当第一个字段的值相同时,再按第二个字段排序,以此类推 。
-- 先按价格降序,价格相同的再按产品名称升序排列SELECT【涩莱克特】 prod_id, prod_price, prod_nameFROM【弗乱】 productsORDER【欧朵】 BY prod_price desc, prod_name ASC【阿斯克】;按列位置和别名排序
除了直接使用列名,还可以使用列在 SELECT【涩莱克特】 子句中的位置序号(从1开始)或定义的别名进行排序 。
-- 按位置排序:按SELECT【涩莱克特】中的第二列(prod_price)降序,第三列(prod_name)升序SELECT【涩莱克特】 prod_id, prod_price, prod_nameFROM【弗乱】 productsORDER【欧朵】 BY 2 desc, 3;
-- 按别名排序SELECT【涩莱克特】 prod_id, prod_price AS price, prod_nameFROM【弗乱】 productsORDER【欧朵】 BY price desc;⚠️ 注意关键细节与技巧
- 子句顺序:在 SQL 语句中,
ORDER【欧朵】 BY子句必须位于所有其他子句之后,即正确的书写顺序是:SELECT【涩莱克特】->FROM【弗乱】->WHERE【威尔】->GROUP【格鲁普】 BY->HAVING【嗨夫营】->ORDER【欧朵】 BY。 - NULL【nò】 值的排序:对于包含 NULL【nò】 值的列进行排序时,NULL【nò】 会被集中排列,但具体出现在结果集的开头还是末尾,不同的数据库管理系统可能有不同的默认行为 。
- 与聚合函数结合使用:
ORDER【欧朵】 BY可以根据聚合函数的结果进行排序,这在数据统计和分析时非常有用 。-- 统计每个产品类型的数量,并按数量降序排列SELECT【涩莱克特】 product_type, COUNT【kàn特】(*)FROM【弗乱】 ProductGROUP【格鲁普】 BY product_typeORDER【欧朵】 BY COUNT【kàn特】(*) desc; - 性能考虑:对经常需要排序的列建立索引,可以显著提高排序查询的速度 。
💎 简单总结
ORDER【欧朵】 BY 是控制查询结果展示顺序的关键。记住:ORDER【欧朵】 BY 默认升序,多列排序时左侧列优先级最高,并且它总是 SELECT【涩莱克特】 语句的最后一个子句。
desc : 降序
asc【阿斯克】:升序
命令操作如下所示:
查看满足条件记录的name和uid 字段的值
mysql> select【涩莱克特】 name , uid from【弗乱】 moershi.user where【威尔】uid is not【呐-特】 null【nò】 and uid between【B豚ing】 100 and 1000 ;+-----------------+------+| name | uid |+-----------------+------+| systemd-network | 192 || polkitd | 999 || chrony | 998 || haproxy | 188 || plj | 1000 |+-----------------+------+5 rows in set【赛特】 (0.00 sec)按照uid升序排序
mysql> select【涩莱克特】 name , uid from【弗乱】 moershi.user where【威尔】uid is not【呐-特】 null【nò】 and uid between【B豚ing】 100 and 1000 order【欧朵】 by uid asc【阿斯克】;+-----------------+------+| name | uid |+-----------------+------+| haproxy | 188 || systemd-network | 192 || chrony | 998 || polkitd | 999 || plj | 1000 |+-----------------+------+5 rows in set【赛特】 (0.00 sec)按照uid降序排序
mysql> select【涩莱克特】 name , uid from【弗乱】 moershi.user where【威尔】uid is not【呐-特】 null【nò】 and uid between【B豚ing】 100 and 1000 order【欧朵】 by uid desc;+-----------------+------+| name | uid |+-----------------+------+| plj | 1000 || polkitd | 999 || chrony | 998 || systemd-network | 192 || haproxy | 188 |+-----------------+------+5 rows in set【赛特】 (0.00 sec)查看2015年1月10号员工编号小于10的工资总额
mysql> select【涩莱克特】 employee【ing普洛伊】_id , date , basic【贝斯克】 , bonus【波诺斯】 , basic【贝斯克】+bonus【波诺斯】 as total【偷ò头ō】 from【弗乱】 moershi.salary【晒了瑞】 where【威尔】date=20150110 and employee【ing普洛伊】_id <= 10;+-------------+------------+-----------------+----------------+-----------------+| employee_id | date | basic【贝斯克】 | bonus【波诺斯】 | total【偷ò头ō】 |+-------------+------------+-----------------+----------------+-----------------+| 2 | 2015-01-10 | 17000 | 10000 | 27000 || 3 | 2015-01-10 | 8000 | 2000 | 10000 || 4 | 2015-01-10 | 14000 | 9000 | 23000 || 6 | 2015-01-10 | 14000 | 10000 | 24000 || 7 | 2015-01-10 | 19000 | 10000 | 29000 |+-------------+------------+-----------------+----------------+-----------------+5 rows in set【赛特】 (0.00 sec)以工资总额升序排 ,总额相同按照员工编号升序排
mysql> select【涩莱克特】 employee【ing普洛伊】_id , basic【贝斯克】+bonus【波诺斯】 as total【偷ò头ō】 from【弗乱】 moershi.salary【晒了瑞】 where【威尔】date=20150110 and employee【ing普洛伊】_id <= 10 order【欧朵】 by total【偷ò头ō】 asc【阿斯克】 ,employee【ing普洛伊】_id asc【阿斯克】;+-------------+-------+| employee_id | total【偷ò头ō】 |+-------------+-------+| 3 | 10000 || 4 | 23000 || 6 | 24000 || 2 | 27000 || 7 | 29000 |+-------------+-------+5 rows in set【赛特】 (0.00 sec)五、分组后条件过滤
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果</n>进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class C highlight;对分组后的组进行筛选: having【嗨夫营】
| 特性 | HAVING【嗨夫营】 子句 | WHERE【威尔】 子句 |
|---|---|---|
| 核心功能 | 对 分组后的组 进行筛选 | 对 分组前的原始数据行 进行筛选 |
| 作用对象 | 由 GROUP【格鲁普】 BY 产生的分组或聚合函数的结果 | 来自表的单行记录 |
| 聚合函数 | 可以且经常 使用(如 HAVING【嗨夫营】 SUM【桑à姆】(amount) > 100) | 不可以 直接使用 |
| 执行顺序 | 在 GROUP【格鲁普】 BY 之后 | 在 GROUP【格鲁普】 BY 之前 |
| 典型应用 | 筛选满足条件的组,例如”总销售额大于1万的部门” | 筛选满足条件的行,例如”年龄大于30岁的员工” |
💡 理解 HAVING【嗨夫营】 的工作原理
你可以将 SQL 查询的执行顺序理解为:FROM【弗乱】 → WHERE【威尔】 → GROUP【格鲁普】 BY → HAVING【嗨夫营】 → SELECT【涩莱克特】 → ORDER【欧朵】 BY 。
- FROM【弗乱】:首先从表中获取数据。
- WHERE【威尔】:接着,
WHERE【威尔】子句会像一道筛子,根据你设定的条件(比如amount > 100)对原始数据行进行过滤,只保留符合条件的行。这一步会在分组之前有效减少需要处理的数据量 。 - GROUP【格鲁普】 BY:然后,数据库会按照指定的列对
WHERE【威尔】过滤后的结果进行分组,将具有相同值的行归到同一组中。 - HAVING【嗨夫营】:最后,
HAVING【嗨夫营】子句登场,它基于聚合函数的结果(例如组的平均工资、总销售额等)对这些分组进行筛选,只保留满足条件的分组 。
🛠️ HAVING【嗨夫营】 的典型用法场景
1. 基础用法:筛选聚合结果 这是 HAVING【嗨夫营】 最经典的场景,用于找出符合某项统计条件的组。
-- 找出总消费金额超过1000元的客户SELECT【涩莱克特】 customer_id, SUM【桑à姆】(amount) AS total【偷ò头ō】_spendingFROM【弗乱】 ordersGROUP【格鲁普】 BY customer_idHAVING【嗨夫营】 SUM【桑à姆】(amount) > 1000;2. 结合 WHERE【威尔】 使用:提高查询效率
你可以同时使用 WHERE【威尔】 和 HAVING【嗨夫营】,让 WHERE【威尔】 先过滤掉不需要的原始数据,减少分组计算的压力,再由 HAVING【嗨夫营】 完成最终筛选 。
-- 找出在2024年下单次数超过5次的客户SELECT【涩莱克特】 customer_id, COUNT【kàn特】(order_id) AS order_count【kàn特】FROM【弗乱】 ordersWHERE【威尔】 YEAR(order_date) = 2024 -- 先筛选出2024年的订单,提高效率GROUP【格鲁普】 BY customer_idHAVING【嗨夫营】 COUNT【kàn特】(order_id) > 5; -- 再筛选出订单数超过5的客户3. 多条件组合
在 HAVING【嗨夫营】 子句中,你可以使用 AND、OR 来连接多个聚合条件 。
-- 找出总消费超过2000元且平均订单金额大于500元的客户SELECT【涩莱克特】 customer_id, SUM【桑à姆】(amount) AS total【偷ò头ō】, AVG(amount) AS avg_amountFROM【弗乱】 ordersGROUP【格鲁普】 BY customer_idHAVING【嗨夫营】 SUM【桑à姆】(amount) > 2000 AND AVG(amount) > 500;⚠️ 常见误区与注意事项
- 不要在 HAVING【嗨夫营】 中过滤非聚合的原始字段:如果过滤条件不涉及聚合计算,只是针对某一行的原始字段,那么应该把它放在
WHERE【威尔】子句,而不是HAVING【嗨夫营】中。放在WHERE【威尔】子句效率更高,因为它能在分组前就减少数据量 。当然,如果该字段是分组字段,则可以在 HAVING【嗨夫营】 中使用 。 - HAVING【嗨夫营】 通常依赖于 GROUP【格鲁普】 BY:
HAVING【嗨夫营】子句通常与GROUP【格鲁普】 BY一起出现。除非你的查询是对整个表进行聚合(例如SELECT【涩莱克特】 COUNT【kàn特】(*) FROM【弗乱】 table【忒-部】 HAVING【嗨夫营】 COUNT【kàn特】(*) > 10),否则缺少GROUP【格鲁普】 BY会导致语法错误 。 - 别名使用:在某些数据库系统中,
HAVING【嗨夫营】子句可以直接使用在SELECT【涩莱克特】中为聚合函数定义的别名,例如HAVING【嗨夫营】 total【偷ò头ō】_spending > 1000,这可以使查询更简洁 。但为了更好的兼容性,直接在 HAVING【嗨夫营】 中写完整的聚合表达式是更稳妥的做法。
💎 简单总结
记住一个核心原则:当你需要对 分组后的统计结果 设置条件时,就是 HAVING【嗨夫营】 子句大显身手的时候。它与 GROUP【格鲁普】 BY 是形影不离的好搭档,一个负责分组,一个负责对分组后的结果进行筛选 。
命令操作如下所示:
查找部门总人数少于10人的部门名称及人数
//第一步,查看所有员工的部门名称select【涩莱克特】 dept_id , name from【弗乱】 moershi.employees【ing普洛伊s】;//第二步,按部门编号分组 统计人名个数mysql> select【涩莱克特】 dept_id , count【kàn特】(name) as numbers【nèn波s】 from【弗乱】 moershi.employees【ing普洛伊s】 group【格鲁普】 by dept_id;+---------+-------------------+| dept_id | numbers【nèn波s】 |+---------+-------------------+| 1 | 8 || 2 | 5 || 3 | 6 || 4 | 55 || 5 | 12 || 6 | 9 || 7 | 35 || 8 | 3 |+---------+-------------------+8 rows in set【赛特】 (0.00 sec)//第三步,查找部门人数少于10人的部门名称及人数mysql> select【涩莱克特】 dept_id , count【kàn特】(name) as numbers【nèn波s】 from【弗乱】 moershi.employees【ing普洛伊s】 group【格鲁普】 by dept_id having【嗨夫营】 numbers【nèn波s】 < 10;+---------+-------------------+| dept_id | numbers【nèn波s】 |+---------+-------------------+| 1 | 8 || 2 | 5 || 3 | 6 || 6 | 9 || 8 | 3 |+---------+-------------------+5 rows in set【赛特】 (0.00 sec)六、分页查询 limit【类梅特】
在 MySQL 中,LIMIT【类梅特】是用于实现分页查询的关键字,它能够精确控制查询结果返回的记录条数和起始位置 。下面这个表格能帮你快速抓住它的核心用法。
| 特性 | 语法格式A | 语法格式B (MySQL 8.0+ 推荐) | 说明 |
|---|---|---|---|
| 基本语法 | LIMIT【类梅特】 offset【哦夫赛特】, row_count | LIMIT【类梅特】 row_count OFFSET【哦夫赛特】 offset【哦夫赛特】 | 从第 offset【哦夫赛特】 条记录开始,返回 row_count 条记录。 |
| 偏移量(offset【哦夫赛特】) | 从 0 开始计数 | 从 0 开始计数 | 例如,offset【哦夫赛特】 为 0 表示从第一条记录开始。 |
| 简化查询 | LIMIT【类梅特】 row_count | - | 等同于 LIMIT【类梅特】 0, row_count,返回前 row_count 条。 |
| 分页公式 | LIMIT【类梅特】 (pageNum - 1) * pageSize, pageSize | LIMIT【类梅特】 pageSize OFFSET【哦夫赛特】 (pageNum - 1) * pageSize | pageNum: 当前页码(从1开始); pageSize: 每页条数。 |
💡 如何使用 LIMIT【类梅特】 进行分页
掌握 LIMIT【类梅特】 的最佳方式就是动手实践。假设我们有一张 users 表,需要实现每页显示10条记录的分页查询。
-
查询第1页数据
-- 使用语法ASELECT【涩莱克特】 * FROM【弗乱】 users ORDER BY id LIMIT【类梅特】 0, 10;-- 使用语法B (推荐,语义更清晰)SELECT【涩莱克特】 * FROM【弗乱】 users ORDER BY id LIMIT【类梅特】 10 OFFSET【哦夫赛特】 0;这两条语句是等价的,都返回最前面的10条记录。
-
查询第2页数据
-- 偏移量 offset【哦夫赛特】 = (2 - 1) * 10 = 10SELECT【涩莱克特】 * FROM【弗乱】 users ORDER BY id LIMIT【类梅特】 10, 10;-- 或SELECT【涩莱克特】 * FROM【弗乱】 users ORDER BY id LIMIT【类梅特】 10 OFFSET【哦夫赛特】 10;这里从第11条记录开始(偏移10条),再取10条记录。
⚠️ 重要提示:分页查询必须与 ORDER BY 子句配合使用,以确保每次查询结果的顺序一致。否则,数据库返回的记录顺序可能是随机的,导致分页混乱。
🚀 如何优化大偏移量下的分页性能
当需要查询的页码非常靠后时(例如 LIMIT【类梅特】 100000, 10),直接使用 LIMIT【类梅特】 会产生性能问题,因为数据库需要先扫描并丢弃掉偏移量指定的前大量记录,效率很低。
一个高效的优化策略是使用基于主键(或唯一索引)的范围查询来替代大的偏移量。这种方法的原理是记录上一页最后一条记录的唯一ID,然后以此为起点获取下一页。
-- 假设已知上一页最后一条记录的id是10000-- 传统的低效写法:SELECT【涩莱克特】 * FROM【弗乱】 table【忒-部】 ORDER BY id LIMIT【类梅特】 10000, 10;-- 优化后的高效写法:SELECT【涩莱克特】 * FROM【弗乱】 table【忒-部】 WHERE【威尔】 id > 10000 ORDER BY id LIMIT【类梅特】 10;这种方法通过索引直接定位到起始位置,跳过了大量数据的扫描,使得查询速度与页码基本无关。它非常适合实现“上一页/下一页”这种连续翻页的场景。
💎 简单总结
LIMIT【类梅特】 是实现MySQL分页的核心。记住:务必搭配 ORDER BY 保证顺序,在需要跳转到很靠后的页面时,尽量使用基于唯一ID的范围查询来优化性能。
命令操作如下所示:
关键字:limit【类梅特】 数字1,数字2
数字1:起始行
数字2:每页显示总行数
例如:
limit【类梅特】 1 ; 显示查询结果的第1行limit【类梅特】 3 ; 显示查询结果的前3行limit【类梅特】 10 ; 显示查询结果的前10行limit【类梅特】 0,1 ; 从查询结果的第1行开始显示,共显示1行limit【类梅特】 3,5 ; 从查询结果的第4行开始显示,共显示5行limit【类梅特】 10,10; 从查询结果的第11行开始显示,共显示10行查看有解释器的用户信息
mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】shell is not【呐-特】 null【nò】 ;只显示查询结果的第1行
mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】shell is not【呐-特】 null【nò】 limit【类梅特】 1;只显示查询结果的前3行
mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】shell is not【呐-特】 null【nò】 limit【类梅特】 3;仅仅显示查询结果的第1行 到 第3 (0 表示查询结果的第1行)
mysql> select【涩莱克特】 * from【弗乱】user where【威尔】shell is not【呐-特】 null【nò】 limit【类梅特】 0,3;从查询结果的第4行开始显示,共显示3行
mysql> select【涩莱克特】 name,uid , gid , shell from【弗乱】user where【威尔】shell is not【呐-特】 null【nò】 limit【类梅特】 3,3;查看uid 号最大的用户名和UID
mysql> select【涩莱克特】 name , uid from【弗乱】 moershi.user order【欧朵】 by uid desc limit【类梅特】 1 ;+-----------+-------+| name | uid |+-----------+-------+| nfsnobody | 65534 |+-----------+-------+1 row in set【赛特】 (0.00 sec)六、新增数据
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果</n>进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class D highlight;命令操作如下所示:
查看表头
desc
在SQL中,desc 是一个常用的关键字,但它根据使用场景的不同,主要有两种截然不同的含义和用法。为了帮助你快速区分,我先用一个表格来总结它的核心用途。
| 用途分类 | 语法/上下文 | 核心功能 | 简要说明 |
|---|---|---|---|
| 查看表结构 | desc table【忒-部】_name; | 显示数据表的详细结构信息(列名、类型、约束等)。 | 主要用于数据库管理和调试,是一个命令。 |
| 指定查询排序 | ORDER BY column【科勒姆】_name desc; | 在查询结果中,将指定列按降序(从大到小)排列。 | 必须与 ORDER BY 子句搭配使用,是一个关键字。 |
📊 作为表结构查看命令
这是 desc 命令最直接的用途,相当于数据表的“说明书”。执行后,它会返回一个包含以下字段的表格,帮助你全面了解表的设计 :
| 输出列 | 含义说明 | 备注/示例 |
|---|---|---|
| Field | 表中每一列(字段)的名称。 | 例如 id, name, email。 |
| Type | 该列字段的数据类型。 | 例如 int(11), varchar(255), date。 |
| Null【nò】 | 该列是否允许存储空值(NULL【nò】)。 | YES 表示允许,NO 表示不允许。 |
| Key | 表示该列是否定义了键(索引)。 | PRI(主键),UNI(唯一索引),MUL(可重复的非唯一索引前导列或唯一索引的组成部分但可含空值),或为空(无索引)。 |
| Default | 该列的默认值。如果未显式赋值且未指定默认值,多数情况下显示 NULL【nò】。 | 例如,可设置为 CURRENT_TIMESTAMP。 |
| Extra | 额外信息,如 auto_increment(自动递增)。 | 常见于主键ID列。 |
如何使用:
-- 查看名为 'employees【ing普洛伊s】' 的表结构desc employees【ing普洛伊s】;💡 如何避免混淆?
记住一个简单的规则:看它的位置。
- 如果
desc后面直接跟着表名(如desc employees【ing普洛伊s】;),它就是查看表结构的命令。 - 如果
desc前面有ORDER BY关键字(如ORDER BY salary【晒了瑞】 desc;),它就是指定降序排序的关键字。
命令操作如下所示:
mysql> desc moershi.user;+---------------------+-------------+------------+-----+---------------------+----------------+| Field | Type | Null【nò】 | Key| Default【迪-佛特】 | Extra |+---------------------+-------------+------------+-----+---------------------+----------------+| id | int | NO | PRI | NULL【nò】 | auto_increment || name | char(20) | YES | | NULL【nò】 | || password | char(1) | YES | | NULL【nò】 | || uid | int | YES | | NULL【nò】 | || gid | int | YES | | NULL【nò】 | || comment【坑门特】 | varchar(50) | YES | | NULL【nò】 | || homedir【home-跌儿】 | varchar(80) | YES | | NULL【nò】 | || shell | char(30) | YES | | NULL【nò】 | |+---------------------+-------------+------------+-----+---------------------+----------------+8 rows in set【赛特】 (0.00 sec)插入1条记录给所有表头赋值
(给所有表头赋值表头可以省略不写)id表头的值不能重复,主键的知识在后边课程里讲
mysql> insert【因涩特】 into【因兔】 moershi.user values【挖柳斯】(40,"jingyaya","x",1001,1001,"teacher","/home/jingyaya","/bin/bash");Query OK, 1 row affected (0.05 sec)查看表记录
mysql> select【涩莱克特】 * from【弗乱】 moershi.user where【威尔】name="jingyaya";+----+----------+----------+------+------+------------------+------------------------+-----------+| id | name | password | uid | gid | comment【坑门特】 | homedir【home-跌儿】 | shell |+----+----------+----------+------+------+------------------+------------------------+-----------+| 40 | jingyaya | x | 1001 | 1001 | teacher | /home/jingyaya | /bin/bash |+----+----------+----------+------+------+------------------+------------------------+-----------+1 row in set【赛特】 (0.00 sec)mysql>插入多行记录给所有列赋值
insert【因涩特】 into【因兔】 moershi.user values【挖柳斯】(41,"jingyaya2","x",1002,1002,"teacher","/home/jingyaya2","/bin/bash"),(42,"jingyaya3","x",1003,1003,"teacher","/home/jingyaya3","/bin/bash");插入1行给指定列赋值,必须写列名,没赋值的列 没有数据 后通过设置的默认值赋值
mysql> insert【因涩特】 into【因兔】 moershi.user(name,uid,shell)values【挖柳斯】("benben",1002,"/sbin/nologin");插入多行给指定列赋值,必须写列名,没赋值的列 没有数据 后通过设置的默认值赋值
mysql> insert【因涩特】 into【因兔】 moershi.user(name,uid,shell)values【挖柳斯】("benben2",1002,"/sbin/nologin"),("benben3",1003,"/sbin/nologin");查看记录
mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】name like "benben%";+----+---------+----------+------+------+-------------------+---------------------+------------------------------+| id | name | password | uid | gid | comment【坑门特】 | homedir【home-跌儿】 | shell |+----+---------+----------+------+------+-------------------+---------------------+------------------------------+| 41 | benben | NULL | 1002 | NULL | NULL | NULL | /sbin/nologin【诺劳京(白话)】 || 42 | benben2 | NULL | 1002 | NULL | NULL | NULL | /sbin/nologin【诺劳京(白话)】 || 43 | benben3 | NULL | 1003 | NULL | NULL | NULL | /sbin/nologin【诺劳京(白话)】 |+----+---------+----------+------+------+-------------------+---------------------+------------------------------+3 rows in set【赛特】 (0.00 sec)使用select【涩莱克特】查询结果赋值(查询表头个数和 插入记录命令表头个数要一致)
mysql> select【涩莱克特】 user from【弗乱】 mysql.user;+------------------+| user |+------------------+| mysql.infoschema || mysql.session || mysql.sys || root |+------------------+4 rows in set【赛特】 (0.00 sec)mysql> insert【因涩特】 into【因兔】 moershi.user(name) (select【涩莱克特】 user from【弗乱】 mysql.user);Query OK, 4 rows affected (0.09 sec)Records: 4 Duplicates: 0 Warnings: 0查看插入后的数据
mysql> select【涩莱克特】 * from【弗乱】 moershi.user where【威尔】name like "mysql%" or name="root";+----+------------------+----------+------+------+-----------------------+------------------------+------------+| id | name | password | uid | gid | comment【坑门特】 | homedir【home-跌儿】 | shell |+----+------------------+----------+------+------+-----------------------+------------------------+------------+| 1 | root | x | 0 | 0 | root | /root | /bin/bash || 26 | mysql | x | 27 | 27 | MySQL Server | /var/lib/mysql | /bin/false || 44 | mysql.infoschema | NULL | NULL | NULL | NULL | NULL | NULL || 45 | mysql.session | NULL | NULL | NULL | NULL | NULL | NULL || 46 | mysql.sys | NULL | NULL | NULL | NULL | NULL | NULL || 47 | root | NULL | NULL | NULL | NULL | NULL | NULL |+----+------------------+----------+------+------+-----------------------+------------------------+------------+6 rows in set【赛特】 (0.00 sec)使用set【赛特】命令赋值
mysql> insert【因涩特】 into【因兔】 moershi.user set【赛特】 name="yaya" , uid=99 , gid=99 ;Query OK, 1 row affected (0.06 sec)mysql> select【涩莱克特】 * from【弗乱】 moershi.user where【威尔】name="yaya";+----+------+----------+------+------+------------------+----------------------+-------+| id | name | password | uid | gid | comment【坑门特】 | homedir【home-跌儿】 | shell |+----+------+----------+------+------+------------------+----------------------+-------+| 28 | yaya | NULL | 99 | 99 | NULL | NULL | NULL |+----+------+----------+------+------+------------------+----------------------+-------+1 row in set【赛特】 (0.00 sec)七、修改数据
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果</n>进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class E highlight;命令操作如下所示:
//修改前查看mysql> select【涩莱克特】 name , comment【坑门特】 from【弗乱】moershi.user where【威尔】id <= 10 ;+----------+--------------------+| name | comment【坑门特】 |+----------+--------------------+| root | root || bin | bin || daemon | daemon || adm | adm || lp | lp || sync | sync || shutdown | shutdown || halt | halt || mail | mail || operator | operator |+----------+--------------------+10 rows in set【赛特】 (0.00 sec)//修改符合条件mysql> update【阿普 dei 特】 moershi.user set【赛特】 comment【坑门特】=NULL【nò】 where【威尔】id <= 10 ;Query OK, 10 rows affected (0.09 sec)Rows matched: 10 Changed: 10 Warnings: 0//修改后查看mysql> select【涩莱克特】 name , comment【坑门特】 from【弗乱】moershi.user where【威尔】id <= 10 ;+----------+------------------+| name | comment【坑门特】 |+----------+------------------+| root | NULL || bin | NULL || daemon | NULL || adm | NULL || lp | NULL || sync | NULL || shutdown | NULL || halt | NULL || mail | NULL || operator | NULL |+----------+------------------+10 rows in set【赛特】 (0.00 sec)[root@localhost ~]#//修改前查看mysql> select【涩莱克特】 name , homedir from【弗乱】moershi.user;+------------------+--------------------+| name | homedir【home-跌儿】|+------------------+--------------------+| root | /root || bin | /bin || daemon | /sbin || adm | /var/adm || lp | /var/spool/lpd || sync | /sbin || shutdown | /sbin || halt | /sbin || mail | /var/spool/mail || operator | /root || games | /usr/games || ftp | /var/ftp || nobody | / || systemd-network | / || dbus | / || polkitd | / || sshd | /var/empty/sshd || postfix | /var/spool/postfix || chrony | /var/lib/chrony || rpc | /var/lib/rpcbind || rpcuser | /var/lib/nfs || nfsnobody | /var/lib/nfs || haproxy | /var/lib/haproxy || plj | /home/plj || apache | /usr/share/httpd || mysql | /var/lib/mysql || bob | NULL || jerrya | NULL || jingyaya | /home/jingyaya || benben | NULL || benben2 | NULL || benben3 | NULL || mysql.infoschema | NULL || mysql.session | NULL || mysql.sys | NULL || root | NULL |+------------------+--------------------+36 rows in set【赛特】 (0.00 sec)//不加条件批量修改mysql> update【阿普 dei 特】 moershi.user set【赛特】 homedir="/student" ;Query OK, 36 rows affected (0.09 sec)Rows matched: 36 Changed: 36 Warnings: 0//修改后查看mysql> select【涩莱克特】 name , homedir from【弗乱】moershi.user;+------------------+---------------------+| name | homedir【home-跌儿】|+------------------+---------------------+| root | /student || bin | /student || daemon | /student || adm | /student || lp | /student || sync | /student || shutdown | /student || halt | /student || mail | /student || operator | /student || games | /student || ftp | /student || nobody | /student || systemd-network | /student || dbus | /student || polkitd | /student || sshd | /student || postfix | /student || chrony | /student || rpc | /student || rpcuser | /student || nfsnobody | /student || haproxy | /student || plj | /student || apache | /student || mysql | /student || bob | /student || jerrya | /student || jingyaya | /student || benben | /student || benben2 | /student || benben3 | /student || mysql.infoschema | /student || mysql.session | /student || mysql.sys | /student || root | /student |+------------------+---------------------+36 rows in set【赛特】 (0.00 sec)八、删除数据
当前任务环节
flowchart LR subgraph BottomRow[单表增删改查] direction LR A[分组查询] --> B[对查询结果</n>进行排序] --> C[分组后条件过滤] --> D[新增数据] --> E[修改数据] --> F[删除数据] end
classDef highlight stroke:#f00,stroke-width:2px; class F highlight;命令操作如下所示:
//删除前查看mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】id <= 10 ;+----+----------+----------+------+------+------------------+---------------------+----------------+| id | name | password | uid | gid | comment【坑门特】 | homedir【home-跌儿】| shell |+----+----------+----------+------+------+------------------+---------------------+----------------+| 1 | root | x | 0 | 0 | NULL | /student | /bin/bash || 2 | bin | x | 1 | 1 | NULL | /student | /sbin/nologin || 3 | daemon | x | 2 | 2 | NULL | /student | /sbin/nologin || 4 | adm | x | 3 | 4 | NULL | /student | /sbin/nologin || 5 | lp | x | 4 | 7 | NULL | /student | /sbin/nologin || 6 | sync | x | 5 | 0 | NULL | /student | /bin/sync || 7 | shutdown | x | 6 | 0 | NULL | /student | /sbin/shutdown || 8 | halt | x | 7 | 0 | NULL | /student | /sbin/halt || 9 | mail | x | 8 | 12 | NULL | /student | /sbin/nologin || 10 | operator | x | 11 | 0 | NULL | /student | /sbin/nologin |+----+----------+----------+------+------+------------------+---------------------+----------------+10 rows in set【赛特】 (0.00 sec)//仅删除与条件匹配的行mysql> delete【迪利特】 from【弗乱】moershi.user where【威尔】id <= 10 ;Query OK, 10 rows affected (0.06 sec)//查不到符合条件的记录了mysql> select【涩莱克特】 * from【弗乱】moershi.user where【威尔】id <= 10 ;Empty set【赛特】 (0.00 sec)九、作业
1 选择题
-
在
employees【ing普洛伊s】表中,要将查询结果按照hire_date降序排列,应使用以下哪个关键字?B A.ASC【阿斯克】B.descC.GROUP【格鲁普】 BYD.HAVING【嗨夫营】 -
若要在
departments【迪帕踢门特s】表中新增一条记录,dept_name为 ‘市场部’,以下 SQL 语句正确的是? A A.INSERT【因涩特】 INTO【因兔】 departments【迪帕踢门特s】 (dept_name) VALUES【挖柳斯】 ('市场部');B.UPDATE【阿普 dei 特】 departments【迪帕踢门特s】 SET【赛特】 dept_name = '市场部';C.DELETE【迪利特】 from【弗乱】departments【迪帕踢门特s】 where【威尔】dept_name = '市场部';D.SELECT * from【弗乱】departments【迪帕踢门特s】 where【威尔】dept_name = '市场部'; -
在
salary【晒了瑞】表中,要统计每个员工的总薪资(basic【贝斯克】与bonus【波诺斯】之和),并按照总薪资降序排列,以下 SQL 语句正确的是?A A.SELECT employee【ing普洛伊】_id, SUM(basic【贝斯克】 + bonus【波诺斯】) AS total【偷ò头ō】_salary【晒了瑞】 from【弗乱】salary【晒了瑞】 GROUP【格鲁普】 BY employee【ing普洛伊】_id ORDER【欧朵】 BY total【偷ò头ō】_salary【晒了瑞】 desc;B.SELECT employee【ing普洛伊】_id, AVG(basic【贝斯克】 + bonus【波诺斯】) AS total【偷ò头ō】_salary【晒了瑞】 from【弗乱】salary【晒了瑞】 GROUP【格鲁普】 BY employee【ing普洛伊】_id ORDER【欧朵】 BY total【偷ò头ō】_salary【晒了瑞】 desc;C.SELECT employee【ing普洛伊】_id, SUM(basic【贝斯克】 + bonus【波诺斯】) AS total【偷ò头ō】_salary【晒了瑞】 from【弗乱】salary【晒了瑞】 ORDER【欧朵】 BY total【偷ò头ō】_salary【晒了瑞】 desc;D.SELECT employee【ing普洛伊】_id, AVG(basic【贝斯克】 + bonus【波诺斯】) AS total【偷ò头ō】_salary【晒了瑞】 from【弗乱】salary【晒了瑞】 ORDER【欧朵】 BY total【偷ò头ō】_salary【晒了瑞】 desc; -
在分组查询中,
HAVING【嗨夫营】子句的作用是?B A. 对分组前的数据进行过滤 B. 对分组后的数据进行过滤 C. 对查询结果进行排序 D. 对数据进行分组 -
要删除
employees【ing普洛伊s】表中employee【ing普洛伊】_id为 10 的记录,以下 SQL 语句正确的是?B A.UPDATE【阿普 dei 特】 employees SET【赛特】 employee_id = NULL【nò】 where【威尔】employee_id = 10;B.DELETE【迪利特】 from【弗乱】employees where【威尔】employee_id = 10;C.SELECT * from【弗乱】employees where【威尔】employee_id = 10;D.INSERT【因涩特】 INTO【因兔】 employees (employee_id) VALUES【挖柳斯】 (10);
2 简答题
-
简述
WHERE子句和HAVING子句的区别。 答:WHERE是对分组前的原始数据行进行筛选HAVING是对分组后的组进行筛选 -
在
UPDATE语句中,SET关键字的作用是什么? 答:SET关键字的作用是指定要修改的内容 -
新增数据时,
INSERT INTO语句有哪几种常见的使用方式? 答:1、向表中插入一条完整记录。 2、向表中插入部分字段的值(未指定的字段将采用默认值或自动生成的值,如自增主键)。 3、一次性插入多条记录(批量插入)。 4、结合 SELECT语句,将查询结果插入到另一个表中。
3 操作题
- 在
employees表中新增一条记录,name为 ‘李四’,hire_date为 ‘2025-06-01’,birth_date为 ‘1990-05-10’,email为 ‘lisi@example.com’,phone_number为 ‘13800138000’,dept_id为 2。
mysql> insert into moershi.employees values(134,"李四","2025-06-01","1990-05-10","lisi@example.com",13800138000,2);Query OK, 1 row affected (0.19 sec)
mysql> select * from moershi.employees where employee_id=134;+-------------+--------+------------+------------+------------------+--------------+---------+| employee_id | name | hire_date | birth_date | email | phone_number | dept_id |+-------------+--------+------------+------------+------------------+--------------+---------+| 134 | 李四 | 2025-06-01 | 1990-05-10 | lisi@example.com | 13800138000 | 2 |+-------------+--------+------------+------------+------------------+--------------+---------+1 row in set (0.00 sec)- 将
departments表中dept_id为 3 的dept_name修改为 ‘研发部’。
mysql> select * from moershi.departments where dept_id=3;+---------+-----------+| dept_id | dept_name |+---------+-----------+| 3 | 运维部 |+---------+-----------+1 row in set (0.00 sec)
mysql> update moershi.departments set dept_name="研发部" where dept_id=3;Query OK, 1 row affected (0.13 sec)Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from moershi.departments where dept_id=3;+---------+-----------+| dept_id | dept_name |+---------+-----------+| 3 | 研发部 |+---------+-----------+1 row in set (0.00 sec)- 统计
salary表中每个员工的平均基本工资(basic列),只显示平均基本工资大于 5000 的员工信息,并按照平均基本工资降序排列。
mysql> select employee_id,avg(basic) as 平均工资 from moershi.salary group by employee_id having avg(basic) > 5000 order by 平均工资 desc;+-------------+--------------+| employee_id | 平均工资 |+-------------+--------------+| 103 | 27023.3333 || 19 | 26520.0000 || 104 | 24251.1429 || 117 | 23905.0000 || 8 | 23741.5600 || 86 | 22766.9444 || 112 | 22766.9444 || 10 | 22314.9286 || 16 | 22314.9286 || 102 | 21911.2462 || 119 | 21628.2083 || 118 | 21628.2083 || 7 | 21628.2083 || 106 | 21628.2083 || 105 | 20490.1528 || 122 | 20490.1528 || 91 | 20311.2979 || 76 | 19351.5972 || 88 | 19351.5972 || 2 | 19351.5972 || 95 | 18213.9028 || 98 | 18213.9028 || 123 | 18213.9028 || 92 | 18213.9028 || 17 | 18213.9028 || 107 | 18015.6818 || 80 | 17644.5789 || 1 | 17279.1000 || 5 | 17125.8000 || 110 | 17074.6528 || 108 | 17074.6528 || 13 | 17074.6528 || 131 | 16653.7692 || 125 | 16525.1887 || 127 | 16427.4107 || 81 | 15991.9286 || 4 | 15936.5972 || 6 | 15936.5972 || 11 | 15936.5972 || 12 | 15506.8125 || 85 | 14798.0417 || 79 | 14798.0417 || 99 | 14798.0417 || 75 | 13760.0417 || 129 | 13659.9861 || 94 | 13659.9861 || 96 | 13659.9861 || 124 | 13659.9861 || 100 | 13659.9861 || 9 | 12931.4545 || 115 | 12521.2500 || 14 | 11383.2083 || 120 | 11044.4242 || 15 | 11044.4242 || 128 | 10337.5000 || 78 | 10244.4722 || 126 | 10244.4722 || 84 | 10244.4722 || 113 | 10244.4722 || 132 | 10244.4722 || 83 | 9106.9444 || 121 | 9106.9444 || 116 | 9106.9444 || 3 | 9106.9444 || 130 | 9106.9444 || 82 | 7967.6944 || 93 | 7967.6944 || 109 | 7967.6944 || 77 | 7967.6944 || 87 | 7281.6410 || 89 | 6829.6389 || 101 | 6829.6389 || 18 | 6829.6389 || 97 | 6829.6389 || 114 | 5690.9028 || 111 | 5690.9028 || 90 | 5690.9028 || 133 | 5690.9028 |+-------------+--------------+78 rows in set (0.01 sec)
mysql>要统计 `salary` 表中每个员工的平均基本工资,并筛选出平均工资大于5000且按降序排列,可以使用以下SQL语句。其核心在于使用 `GROUP BY` 对员工分组,使用 `HAVING` 对分组后的聚合结果进行过滤。### 🔍 查询语句及说明完整的SQL查询语句如下:SELECT employee_id, AVG(basic) AS avg_basic_salaryFROM salaryGROUP BY employee_idHAVING AVG(basic) > 5000ORDER BY avg_basic_salary DESC;下面这个表格详细解释了每个子句的作用:| SQL 子句 | 作用说明 | 备注 || :--- | :--- | :--- || `SELECT employee_id, AVG(basic) AS avg_basic_salary` | 选择员工编号和计算出的平均基本工资,并为平均工资列设置别名 `avg_basic_salary`。 | `AVG()` 是聚合函数,用于计算平均值。 || `FROM salary` | 指定查询的数据来源是 `salary` 表。 | - || `GROUP BY employee_id` | 根据 `employee_id` 进行分组。这是使用聚合函数(如`AVG`)的前提,它将数据按每个员工归类。 | 确保 `SELECT` 中非聚合列在 `GROUP BY` 中出现。 || `HAVING AVG(basic) > 5000` | 对分组(即每个员工)后的结果进行筛选,只保留平均基本工资大于5000的记录。**`HAVING` 专门用于过滤聚合函数的结果**。 | 这与在分组前过滤行的 `WHERE` 子句有本质区别。 || `ORDER BY avg_basic_salary DESC` | 将最终结果按照平均基本工资(别名)从高到低进行排序。 | `DESC` 表示降序,`ASC` 表示升序。 |### 💡 关键点与技巧- **`HAVING` 与 `WHERE` 的区别**:这是编写此类查询的关键。`WHERE` 子句在 **分组之前** 过滤原始数据行,并且其条件中不能直接使用聚合函数(如 `AVG(), COUNT()`)。`HAVING` 子句在 **分组之后** 过滤分组结果,条件通常包含聚合函数。如果你的过滤条件不涉及聚合计算(例如 `employee_id > 100`),则应使用 `WHERE`,这能先减少数据量,提升查询效率。- **使用列别名**:在 `ORDER BY` 子句中,我们可以直接使用在 `SELECT` 中定义的列别名(`avg_basic_salary`),这使得语句更简洁易读。但请注意,在某些数据库系统中,`HAVING` 子句可能不支持直接使用别名,此时需要重复聚合函数(如 `HAVING AVG(basic) > 5000`),如示例中所示。### 📚 扩展思考你可以通过修改聚合函数和条件来满足不同的统计需求。例如:- 统计每个员工的**总工资**(假设还有其他工资项):`SELECT employee_id, SUM(basic + bonus) AS total_salary FROM salary GROUP BY employee_id HAVING SUM(basic + bonus) > 10000 ORDER BY total_salary DESC;`- 先通过 `WHERE` 子句排除某些记录后再进行分组统计,例如,只统计某个月份之后的数据。
- 删除
employees表中dept_id为 4 的所有记录。
mysql> --查询删除前数据mysql> select * from moershi.employees where dept_id=4;+-------------+-----------+------------+------------+---------------------------+--------------+---------+| employee_id | name | hire_date | birth_date | email | phone_number | dept_id |+-------------+-----------+------------+------------+---------------------------+--------------+---------+| 20 | 蒋红 | 2017-12-29 | 1978-01-24 | jianghong@tedu.cn | 15852915398 | 4 || 21 | 曹宁 | 2004-06-07 | 1988-01-05 | caoning@tedu.cn | 14513022304 | 4 || 22 | 吕刚 | 2014-01-18 | 1980-06-24 | lvgang@tedu.cn | 18136987619 | 4 || 23 | 王莉 | 2021-02-04 | 1972-12-19 | wangli@moershi.com | 15376329290 | 4 || 24 | 邓秀芳 | 2020-09-08 | 1980-12-12 | dengxiufang@moershi.com | 15884927117 | 4 || 25 | 邵佳 | 2011-08-15 | 1978-11-28 | shaojia@tedu.cn | 13296016750 | 4 || 26 | 党丽 | 2014-03-14 | 1978-12-30 | dangli@moershi.com | 13166234580 | 4 || 27 | 梁勇 | 2007-01-19 | 1997-11-12 | liangyong@tedu.cn | 15307868657 | 4 || 28 | 郑秀珍 | 2011-04-28 | 1972-09-01 | zhengxiuzhen@moershi.com | 14543186401 | 4 || 29 | 胡秀云 | 2003-11-03 | 2000-05-14 | huxiuyun@tedu.cn | 18212266720 | 4 || 30 | 邢淑兰 | 2011-07-02 | 1989-06-17 | xingshulan@moershi.com | 13032270370 | 4 || 31 | 刘海燕 | 2018-01-06 | 1982-08-21 | liuhaiyan@moershi.com | 15064354651 | 4 || 32 | 冯建国 | 2014-05-28 | 1980-01-26 | fengjianguo@tedu.cn | 13344957322 | 4 || 33 | 曹杰 | 2017-01-04 | 1975-07-03 | caojie@moershi.com | 13244741822 | 4 || 34 | 苗桂花 | 2001-10-06 | 1974-01-26 | miaoguihua@tedu.cn | 18124884107 | 4 || 35 | 袁建平 | 2020-09-17 | 1990-07-25 | yuanjianping@moershi.com | 13580624147 | 4 || 36 | 黄淑兰 | 2019-08-29 | 1988-02-08 | huangshulan@tedu.cn | 14568738205 | 4 || 37 | 朱淑兰 | 2002-04-26 | 1977-12-10 | zhushulan@tedu.cn | 13635439422 | 4 || 38 | 曹凯 | 2006-07-23 | 1995-11-07 | caokai@moershi.com | 15172694474 | 4 || 39 | 张倩 | 2009-10-27 | 2000-04-27 | zhangqian@tedu.cn | 15053279648 | 4 || 40 | 王淑珍 | 2013-09-21 | 1995-05-06 | wangshuzhen@tedu.cn | 18583012709 | 4 || 41 | 陈玉 | 2011-08-20 | 1985-06-14 | chenyu@tedu.cn | 13632957562 | 4 || 42 | 陈玉英 | 2020-10-03 | 1996-05-19 | chenyuying@tedu.cn | 15100520600 | 4 || 43 | 王波 | 2008-06-14 | 1979-05-26 | wangbo@tedu.cn | 15236208918 | 4 || 44 | 黄文 | 2010-06-05 | 1983-01-30 | huangwen@tedu.cn | 13688719481 | 4 || 45 | 陈刚 | 2010-10-26 | 1978-05-10 | chengang@tedu.cn | 13679861175 | 4 || 46 | 罗建华 | 2004-06-20 | 1989-02-23 | luojianhua@tedu.cn | 13176305978 | 4 || 47 | 黄建平 | 2009-04-09 | 1995-07-15 | huangjianping@moershi.com | 18517722322 | 4 || 48 | 范秀英 | 2001-03-13 | 1996-06-01 | fanxiuying@moershi.com | 18252125689 | 4 || 49 | 李平 | 2003-10-28 | 1998-07-24 | liping@tedu.cn | 18526198243 | 4 || 50 | 臧龙 | 2011-10-06 | 1976-05-11 | zanglong@moershi.com | 13474064425 | 4 || 51 | 吴静 | 2017-09-26 | 1983-08-04 | wujing@tedu.cn | 14762137325 | 4 || 52 | 张冬梅 | 2010-10-26 | 1982-05-09 | zhangdongmei@tedu.cn | 13690746261 | 4 || 53 | 邢成 | 2018-05-07 | 1991-07-13 | xingcheng@moershi.com | 13983238261 | 4 || 54 | 孙丹 | 2012-01-28 | 1997-02-22 | sundan@moershi.com | 13684254376 | 4 || 55 | 梁静 | 2013-03-21 | 1995-05-20 | liangjing@moershi.com | 18077866993 | 4 || 56 | 陈洁 | 2006-08-17 | 1977-04-23 | chenjie@tedu.cn | 14737309895 | 4 || 57 | 许辉 | 2014-09-16 | 1992-10-21 | xuhui@moershi.com | 13143391754 | 4 || 58 | 张伟 | 2007-06-22 | 1999-04-30 | zhangwei@moershi.com | 18172302428 | 4 || 59 | 钟倩 | 2011-02-23 | 1983-06-28 | zhongqian@moershi.com | 18611282765 | 4 || 60 | 贺磊 | 2001-06-19 | 1993-02-07 | helei@tedu.cn | 15897839325 | 4 || 61 | 沈秀梅 | 2003-04-27 | 1992-08-13 | shenxiumei@tedu.cn | 18044302910 | 4 || 62 | 林刚 | 2007-09-19 | 1990-09-23 | lingang@tedu.cn | 13355063263 | 4 || 63 | 王玉华 | 2016-10-09 | 1973-09-14 | wangyuhua@tedu.cn | 18010485419 | 4 || 64 | 徐金凤 | 2015-09-09 | 1972-01-31 | xujinfeng@tedu.cn | 15124816733 | 4 || 65 | 张淑英 | 2010-11-08 | 1996-09-12 | zhangshuying@moershi.com | 18846908114 | 4 || 66 | 罗岩 | 2001-06-13 | 1986-10-17 | luoyan@tedu.cn | 14544559539 | 4 || 67 | 潘玲 | 2017-08-31 | 1993-02-18 | panling@tedu.cn | 13311560534 | 4 || 68 | 柴冬梅 | 2003-11-24 | 1976-01-01 | chaidongmei@moershi.com | 14506733843 | 4 || 69 | 谢莹 | 2017-12-06 | 1996-04-01 | xieying@tedu.cn | 15137904023 | 4 || 70 | 傅雪 | 2001-04-23 | 1976-09-06 | fuxue@moershi.com | 15210059051 | 4 || 71 | 龙旭 | 2019-05-15 | 1974-11-02 | longxu@moershi.com | 15155879580 | 4 || 72 | 赵杰 | 2004-05-08 | 1984-04-06 | zhaojie@tedu.cn | 13466360255 | 4 || 73 | 王璐 | 2003-09-22 | 2000-02-01 | wanglu@moershi.com | 13056592974 | 4 || 74 | 赵成 | 2012-11-30 | 1985-10-11 | zhaocheng@tedu.cn | 18545824587 | 4 |+-------------+-----------+------------+------------+---------------------------+--------------+---------+55 rows in set (0.00 sec)
mysql> --外键约束,先删除salary表对应数据mysql> delete from moershi.salary where employee_id in (select employee_id from moershi.employees where dept_id=4);Query OK, 3308 rows affected (0.36 sec)
mysql> --删除employees表dept_id=4的数据mysql> delete from moershi.employees where dept_id=4;Query OK, 55 rows affected (0.22 sec)
mysql> --查询删除后结果mysql> select * from moershi.employees where dept_id=4;Empty set (0.00 sec)您在删除 `dept_id=4` 的部门记录时遇到了外键约束错误,这表明有其他表(比如 `salary` 表)的数据通过外键关联着这个部门的员工记录。下面为您梳理解决方案。### 🔍 理解错误原因错误信息 `Cannot delete or update a parent row: a foreign key constraint fails` 表明,由于外键约束的存在,您无法直接删除 `employees` 表中 `dept_id=4` 的记录。这通常是数据库为了维护数据一致性而设定的保护机制,防止出现“孤悬数据”(例如,工资记录对应的员工信息不存在了)。核心问题在于,`salary` 表通过 `employee_id` 字段关联了 `employees` 表。当您尝试删除 `employees` 表中的某条员工记录时,如果 `salary` 表中还存在与之对应的工资记录,数据库就会阻止这个删除操作。### ✅ 解决方案选择您可以根据具体情况选择下表列出的方法来解决这个问题:| 解决方案 | 核心操作 | 适用场景 || :--- | :--- | :--- || **先删关联数据** | 先手动删除 `salary` 表中相关的员工工资记录,再删除 `employees` 表中的员工记录。 | 希望精确控制删除范围,或数据库未设置级联删除时。 || **临时禁用约束** | 暂时关闭外键约束检查,执行删除操作后重新开启。 | 需要快速处理数据,且确认删除不会破坏业务逻辑;**需谨慎使用**。 || **设置级联删除** | 修改外键约束,设置 `ON DELETE CASCADE`。 | 希望一劳永逸,后续删除员工能自动同步删除其工资记录。 |### ⚠️ 重要操作步骤与提醒无论选择哪种方法,**在进行任何删除或结构修改操作前,强烈建议备份数据**。- **方案一:先删除关联数据**这种方法思路直接,即先清除子表(`salary`)中的相关记录,再删除父表(`employees`)中的记录。```sql-- 1. 首先删除 salary 表中所有属于 dept_id=4 部门的员工的工资记录DELETE FROM moershi.salaryWHERE employee_id IN (SELECT employee_id FROM moershi.employees WHERE dept_id = 4);-- 2. 然后再删除 employees 表中 dept_id=4 的员工记录DELETE FROM moershi.employees WHERE dept_id = 4;
方案二:临时禁用外键约束 此方法通过
SET FOREIGN_KEY_CHECKS = 0;临时关闭外键约束检查,操作完成后务必记得重新开启。-- 临时禁用外键约束检查SET FOREIGN_KEY_CHECKS = 0;-- 执行删除操作DELETE FROM moershi.employees WHERE dept_id = 4;-- 立即重新启用外键约束检查SET FOREIGN_KEY_CHECKS = 1;注意:这种方法会暂时破坏数据库的引用完整性,如果操作不当,可能导致数据不一致,请务必确保在可控范围内使用。
方案三:设置级联删除(长远之计) 级联删除是一种“一劳永逸”的解决方案。设置后,当您删除
employees表中的某位员工时,数据库会自动删除salary表中与之相关的所有工资记录。 首先,你需要删除原有的外键约束,然后重新添加一个带有级联删除选项的新约束。-- 1. 先删除现有的外键约束(需要知道约束名称,错误信息中提示是 `emp_id_fk`)ALTER TABLE moershi.salary DROP FOREIGN KEY emp_id_fk;-- 2. 重新添加外键约束,并设置级联删除ALTER TABLE moershi.salaryADD CONSTRAINT emp_id_fkFOREIGN KEY (employee_id) REFERENCES employees(employee_id)ON DELETE CASCADE;设置完成后,您再次执行
DELETE FROM moershi.employees WHERE dept_id=4;,数据库就会自动处理关联的工资记录了。💡 决策建议与总结
- 若当前操作是临时的、一次性的,建议使用方案一(先删关联数据),它最清晰、可控。
- 若经常需要进行此类关联删除,建议使用方案三(设置级联删除),一劳永逸,但务必理解其自动删除的后果。
- 方案二(临时禁用约束) 风险较高,通常用于数据迁移或清理等特定场景,日常操作请谨慎选择。
请根据您的具体需求选择最合适的方法。如果对约束名称或具体操作步骤仍有疑问,可以随时追问。
- 从
employees表中查询出所有在 2025 年入职的员工信息,并按照hire_date升序排列。
mysql>mysql> select * from moershi.employees where year(hire_date) = 2025 order by hire_date asc;+-------------+--------+------------+------------+------------------+--------------+---------+| employee_id | name | hire_date | birth_date | email | phone_number | dept_id |+-------------+--------+------------+------------+------------------+--------------+---------+| 134 | 李四 | 2025-06-01 | 1990-05-10 | lisi@example.com | 13800138000 | 2 |+-------------+--------+------------+------------+------------------+--------------+---------+1 row in set (0.00 sec)
mysql>要从 `employees` 表中查询出所有在 2025 年入职的员工信息并按入职日期升序排列,可以使用以下 SQL 语句:```sqlSELECT *FROM employeesWHERE YEAR(hire_date) = 2025ORDER BY hire_date ASC;```下面是这个查询语句中各部分作用的详细说明:| SQL 部分 | 作用说明 | 备注 || :--- | :--- | :--- || `SELECT *` | 选择所有列 | 返回员工表的所有字段信息。 || `FROM employees` | 指定数据来源 | 从名为 `employees` 的表中查询数据。 || `WHERE YEAR(hire_date) = 2025` | 筛选条件 | **`YEAR()` 函数用于从日期中提取年份**,这里限定只查询入职年份为 2025 年的记录。 || `ORDER BY hire_date ASC` | 排序结果 | 按照 `hire_date`(入职日期)字段进行升序排列。`ASC` 表示升序,可以省略,因为它是默认的排序方式。 |### 💡 其他可行的日期筛选方法除了使用 `YEAR()` 函数,你也可以考虑以下方法来实现相同的筛选目标,它们在特定场景下可能更有用:- **使用 `BETWEEN` 指定日期范围**你可以明确指定入职日期的起止范围来筛选2025年的记录:```sqlSELECT *FROM employeesWHERE hire_date BETWEEN '2025-01-01' AND '2025-12-31'ORDER BY hire_date;```- **使用 `DATE_FORMAT()` 函数**`DATE_FORMAT()` 函数功能强大,可以按各种格式输出日期。用它来提取年份部分进行匹配也是一种方法:```sqlSELECT *FROM employeesWHERE DATE_FORMAT(hire_date, '%Y') = '2025'ORDER BY hire_date;```