当前位置: 首页 > news >正文

MYSQL:从增删改查到高级查询

文章目录

  • MYSQL:从增删改查到高级查询
    • 1. 写在前面
    • 2. CRUD 核心概念
    • 3. Create:新增数据
      • 3.1 语法结构
      • 3.2 实践一下
        • 3.2.1 单行全列插入
        • 3.2.2 单行指定列插入
        • 3.2.3 多行数据一次性插入
    • 4. Retrieve:检索数据
      • 4.1 语法概览
      • 4.2 准备实验数据
      • 4.3 `SELECT` 子句:选择你想要的列
        • 4.3.1 全列查询
        • 4.3.2 指定列查询
        • 4.3.3 查询字段为表达式
        • 4.3.4 为查询结果指定别名
        • 4.3.5 结果去重查询
      • 4.4 `WHERE` 子句:筛选你想要的行
        • 4.4.1 比较运算符
        • 4.4.2 逻辑运算符
        • 4.4.3 实践一下
      • 4.5 `ORDER BY` 子句:按规则排序
        • 4.5.1 实践一下
      • 4.6 `LIMIT` 子句:分页查询
        • 4.6.1 语法结构
        • 4.6.2 实践一下
    • 5. Update:修改数据
      • 5.1 语法结构
      • 5.2 实践一下
    • 6. Delete:删除数据
      • 6.1 语法结构
      • 6.2 实践一下
    • 7. Truncate:清空表
      • 7.1 语法结构
      • 7.2 `TRUNCATE` vs. `DELETE`
    • 8. 插入查询结果
      • 8.1 语法结构
      • 8.2 实践一下:数据去重
    • 9. 聚合函数
      • 9.1 实践一下
    • 10. `GROUP BY` 分组查询
      • 10.1 准备实验数据
      • 10.2 实践一下
      • 10.3 `HAVING` 子句:对分组结果进行过滤
      • 10.4 `HAVING` 与 `WHERE` 的区别

MYSQL:从增删改查到高级查询

1. 写在前面

掌握数据库的操作是每一位后端开发者的基本功。这其中,最核心、最频繁的就是针对数据的“增、删、改、查”,我们通常称之为 CRUD (Create, Retrieve, Update, Delete)。

本文将从最基础的 CRUD 操作出发,逐步深入到查询中的排序、分页、聚合、分组等高级用法,帮助大家构建一个完整、扎实的 MySQL 数据操作知识体系。我们的目标是:

  • 熟练运用 SQL 语句完成对数据的增删改查。
  • 掌握 COUNT, SUM, AVG 等常用聚合函数。
  • 理解分组查询 GROUP BY 的原理,并能结合 HAVING 子句进行二次过滤。

2. CRUD 核心概念

CRUD 是四个操作的缩写,它们构成了与数据库交互的基础:

  • Create (创建): 向数据库表中添加新的记录。
  • Retrieve (读取): 从数据库表中查询并获取数据。
  • Update (更新): 修改数据库表中的已有记录。
  • Delete (删除): 从数据库表中移除记录。

3. Create:新增数据

我们使用 INSERT 语句向表中添加新数据。

3.1 语法结构

INSERT [INTO] table_name[(column [, column] ...)]
VALUES(value_list) [, (value_list)] ...

value_list 指的是与前面列定义相对应的值列表,例如 (1, '张三')

3.2 实践一下

首先,我们创建一个用于演示的 users 表。

-- 创建一个简单的用户表
CREATE TABLE users (id BIGINT,name VARCHAR(20) COMMENT '用户名'
);

在这里插入图片描述

3.2.1 单行全列插入

这是最直接的插入方式,值的顺序和数量必须与表定义的列完全一致。

-- 插入第一条记录
INSERT INTO users VALUES (1, '张三');
-- 插入第二条记录
INSERT INTO users VALUES (2, '李四');

在这里插入图片描述

3.2.2 单行指定列插入

更推荐的方式是明确指定要插入的列,这样即使表结构发生变化(如增加新列),SQL 语句也无需修改。

-- 明确指定要为 id 和 name 列插入值
INSERT INTO users(id, name) VALUES (3, '王五');

3.2.3 多行数据一次性插入

为了提升效率,我们可以用一条 INSERT 语句插入多行数据。

-- 一次性插入两行数据
INSERT INTO users(id, name) VALUES (4, '赵六'), (5, '钱七');

在这里插入图片描述

小思考:单次插入多行 vs. 多次插入单行

为什么推荐一次性插入多行数据?这主要是出于性能考虑。

  1. 减少网络开销: 每次执行 SQL 都需要通过网络与数据库服务器通信。合并成一条语句可以显著减少通信次数。
  2. 降低事务开销: 数据库每执行一条 INSERT 语句,通常会隐式地开启和关闭一个事务。事务的创建和销毁本身是有资源消耗的。将多次插入合并为一次,意味着只在一个事务内完成,效率更高。
  3. 优化磁盘 I/O: 数据库系统可以对批量插入进行优化,减少磁盘写入的次数。

因此,在业务允许的情况下,将多条插入操作合并成一条,是一个非常有效的性能优化手段。

4. Retrieve:检索数据

SELECT 是我们与数据库打交道最频繁的语句,它的功能也最为强大和复杂。

4.1 语法概览

SELECT[DISTINCT] select_expr [, select_expr] ...[FROM table_references][WHERE where_condition][GROUP BY {col_name | expr}, ...][HAVING where_condition][ORDER BY {col_name | expr } [ASC | DESC], ... ][LIMIT {[offset,] row_count | row_count OFFSET offset}]

4.2 准备实验数据

为了更好地演示查询操作,我们先创建一张学生成绩表 exam 并填充一些数据。

-- 创建表结构
CREATE TABLE exam (id BIGINT,name VARCHAR(20) COMMENT '同学姓名',chinese FLOAT COMMENT '语文成绩',math FLOAT COMMENT '数学成绩',english FLOAT COMMENT '英语成绩'
);-- 插入测试数据
INSERT INTO exam (id, name, chinese, math, english) VALUES
(1, '唐三藏', 67, 98, 56),
(2, '孙悟空', 87, 78, 77),
(3, '猪悟能', 88, 98, 90),
(4, '曹孟德', 82, 84, 67),
(5, '刘玄德', 55, 85, 45),
(6, '孙权', 70, 73, 78),
(7, '宋公明', 75, 65, 30);

在这里插入图片描述

4.3 SELECT 子句:选择你想要的列

4.3.1 全列查询

使用 * 通配符可以查询表中的所有列。

-- 查询 exam 表中的所有记录和所有列
SELECT * FROM exam;

在实际项目中应避免使用 SELECT *。因为它会查询所有列,可能导致不必要的数据传输和性能开销。最佳实践是明确指定你需要的列。

在这里插入图片描述

4.3.2 指定列查询

明确列出你希望查询的字段。

-- 只查询学生的编号、姓名和语文成绩
SELECT id, name, chinese FROM exam;

此时返回列的顺序由你在 SELECT 语句中指定的顺序决定,与表结构的原始顺序无关。

在这里插入图片描述

4.3.3 查询字段为表达式

SELECT 的列表不仅可以是列名,还可以是包含计算的表达式。

-- 查询每个学生的总分
SELECT id, name, chinese + math + english FROM exam;

在这里插入图片描述

4.3.4 为查询结果指定别名

使用 AS 关键字可以为查询结果的列指定一个更具可读性的别名。

-- 为总分这一列表达式指定别名为“总分”
SELECT id, name, chinese + math + english AS '总分' FROM exam;

AS 关键字可以省略。如果别名中包含空格或特殊字符,需要用单引号或双引号包裹。

在这里插入图片描述

4.3.5 结果去重查询

使用 DISTINCT 关键字可以去除结果集中的重复行。

-- 查询所有出现过的数学成绩,并去除重复值
SELECT DISTINCT math FROM exam;

在这里插入图片描述

DISTINCT 会作用于所有指定的查询列,只有当多行的所有列值都完全相同时,才会被视为重复行。

4.4 WHERE 子句:筛选你想要的行

WHERE 子句用于设置查询的过滤条件,只有满足条件的行才会被返回。

4.4.1 比较运算符
运算符说明
>, >=, <, <=大于,大于等于,小于,小于等于
=等于。注意: NULL = NULL 的结果是 NULL,而不是 TRUE
<=>安全等于。这个运算符可以安全地比较 NULL 值,NULL <=> NULL 的结果是 TRUE
!=, <>不等于
BETWEEN a AND b范围匹配,等价于 >= a AND <= b
IN (val1, val2, ...)匹配列表中的任意一个值。
IS NULL / IS NOT NULL判断值是否为 NULL
LIKE模糊匹配。% 代表零个或多个任意字符,_ 代表一个任意字符。
4.4.2 逻辑运算符
运算符说明
AND逻辑与,类似于 Java 中的 &&
OR逻辑或,类似于 Java 中的 `
NOT逻辑非,类似于 Java 中的 !

小思考:运算符优先级

AND 的优先级高于 OR。当一个查询中同时包含 ANDOR 时,为了避免逻辑混乱,强烈建议使用括号 () 来明确指定运算的先后顺序。

4.4.3 实践一下
  • 基本查询:查询英语不及格(小于60分)的同学。

    SELECT name, english FROM exam WHERE english < 60;
    

    在这里插入图片描述

  • ANDOR:查询语文成绩大于80分 英语成绩也大于80分的同学。

    SELECT * FROM exam WHERE chinese > 80 AND english > 80;
    

    在这里插入图片描述

  • 范围查询:查询语文成绩在 [80, 90] 分区间的同学。

    -- 使用 BETWEEN AND
    SELECT name, chinese FROM exam WHERE chinese BETWEEN 80 AND 90;
    -- 使用 AND,效果相同
    SELECT name, chinese FROM exam WHERE chinese >= 80 AND chinese <= 90;
    

    在这里插入图片描述

  • IN 列表查询:查询数学成绩是 78、98 或 99 分的同学。

    SELECT name, math FROM exam WHERE math IN (78, 98, 99);
    

    在这里插入图片描述

  • 模糊查询 LIKE:查询所有姓“孙”的同学。

    SELECT * FROM exam WHERE name LIKE '孙%';
    

    在这里插入图片描述

  • NULL 值查询:查询没有英语成绩的同学。

    -- 准备一条包含 NULL 的数据
    INSERT INTO exam VALUES (8, '张飞', 27, 0, NULL);
    -- 使用 IS NULL 进行查询
    SELECT * FROM exam WHERE english IS NULL;
    

    在这里插入图片描述

小思考:WHERE 子句中为什么不能使用别名?

这是一个非常经典的疑问。我们尝试执行 SELECT name, chinese + math + english AS total FROM exam WHERE total < 200; 会发现报错。

这与 SQL 的执行顺序有关。一个查询语句的逻辑执行顺序大致如下:

  1. FROM:确定要查询的表。
  2. WHERE:根据条件筛选行。
  3. GROUP BY:对筛选后的行进行分组。
  4. HAVING:对分组结果进行二次筛选。
  5. SELECT:确定最终要显示的列。
  6. ORDER BY:对最终结果进行排序。

从这个顺序可以看出,WHERE 子句在 SELECT 子句之前执行。当 WHERE 子句工作时,SELECT 中定义的别名(如 total)还不存在,因此无法使用。而 ORDER BYSELECT 之后执行,所以它可以使用别名。

4.5 ORDER BY 子句:按规则排序

ORDER BY 用于对最终的查询结果进行排序。

  • ASC:升序(默认值)。
  • DESC:降序。
4.5.1 实践一下
  • 单列排序:按数学成绩从高到低(降序)排序。

    SELECT name, math FROM exam ORDER BY math DESC;
    

  • 多列排序:先按数学成绩降序排,如果数学成绩相同,再按英语成绩升序排。

    SELECT name, math, english FROM exam ORDER BY math DESC, english ASC;
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 使用别名排序:按总分从高到低排序。

    SELECT name, chinese + math + english AS '总分' FROM exam ORDER BY '总分' DESC;
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小思考:NULL 值的排序行为

在排序时,NULL 值被认为是最小值。因此,在升序(ASC)排列中,NULL 会出现在最前面;在降序(DESC)排列中,NULL 会出现在最后面。

4.6 LIMIT 子句:分页查询

当数据量很大时,一次性返回所有结果是不现实的。LIMIT 子句可以帮助我们实现分页,每次只查询一部分数据。

4.6.1 语法结构
-- 写法一:从第 start 行开始,取 num 行
LIMIT start, num;-- 写法二(推荐):取 num 行,从第 offset 行开始(offset 从 0 算起)
LIMIT num OFFSET offset;
4.6.2 实践一下

假设每页显示3条记录:

  • 查询第一页

    SELECT * FROM exam ORDER BY id ASC LIMIT 3 OFFSET 0;
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 查询第二页

    SELECT * FROM exam ORDER BY id ASC LIMIT 3 OFFSET 3;
    

小思考:分页公式

假设当前页码为 page_num(从1开始),每页大小为 page_size,那么 OFFSET 的计算公式为:
offset = (page_num - 1) * page_size

5. Update:修改数据

我们使用 UPDATE 语句来修改表中的现有数据。

5.1 语法结构

UPDATE table_name
SET column1 = value1, column2 = value2, ...
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count];

5.2 实践一下

  • 将孙悟空同学的数学成绩变更为 80 分。

    UPDATE exam SET math = 80 WHERE name = '孙悟空';
    

  • 将总成绩倒数前三的同学的数学成绩加上 30 分。

    UPDATE exam SET math = math + 30 ORDER BY chinese + math + english ASC LIMIT 3;
    

小提醒:

UPDATE 语句如果缺少 WHERE 子句,将会更新表中的所有行! 这是一个极其危险的操作,在执行前务必再三确认。

6. Delete:删除数据

我们使用 DELETE 语句来删除表中的记录。

6.1 语法结构

DELETE FROM table_name
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count];

6.2 实践一下

  • 删除孙悟空同学的考试成绩。
    DELETE FROM exam WHERE name = '孙悟空';
    

小提醒:

UPDATE 类似,DELETE 语句如果缺少 WHERE 子句,将会删除表中的所有数据! 请务必谨慎操作。

小思考:物理删除 vs. 逻辑删除

DELETE 执行的是 物理删除,数据会从磁盘上被移除(虽然可以通过日志恢复,但过程复杂)。在生产环境中,对于核心业务数据,我们通常不建议直接使用 DELETE

更安全的做法是 逻辑删除。这通常通过在表中增加一个状态字段(如 is_deletedstatus 等)来实现。当需要删除一条记录时,我们执行的是 UPDATE 操作,将该字段的值标记为“已删除”(例如 is_deleted = 1)。

这样做的好处是数据依然保留在数据库中,可以随时追溯和恢复,业务代码在查询时只需增加一个 WHERE is_deleted = 0 的条件即可。

7. Truncate:清空表

TRUNCATE TABLE 语句用于快速删除一个表中的所有数据。

7.1 语法结构

TRUNCATE [TABLE] table_name;

7.2 TRUNCATE vs. DELETE

虽然 TRUNCATE 和不带 WHEREDELETE 都能清空表数据,但它们有本质区别:

特性DELETE FROM table_nameTRUNCATE TABLE table_name
操作类型DML (数据操作语言)DDL (数据定义语言)
执行速度慢,逐行删除非常快,直接释放数据页
事务支持支持,可回滚不支持,操作无法回滚
自增重置不重置 AUTO_INCREMENT重置 AUTO_INCREMENT 计数器
触发器会触发 DELETE 触发器不会触发触发器

简单来说,如果你只是想彻底清空一个表并重置其状态,TRUNCATE 是更高效的选择。但如果需要可恢复的删除操作,则应使用 DELETE

8. 插入查询结果

INSERT ... SELECT ... 是一个非常实用的功能,它允许我们将一个 SELECT 查询的结果直接插入到另一个表中。

8.1 语法结构

INSERT INTO table_name [(column [, column ...])]
SELECT ...;

8.2 实践一下:数据去重

一个经典的应用场景是为已有的大量重复数据进行去重。

  1. 准备数据:假设 t_recored 表中存在大量重复记录。

    CREATE TABLE t_recored (id INT, name VARCHAR(20));
    INSERT INTO t_recored VALUES
    (100, 'aaa'), (100, 'aaa'),
    (200, 'bbb'), (200, 'bbb'), (200, 'bbb'),
    (300, 'ccc');
    

  2. 创建新表并插入去重后的数据

    -- 创建一个与原表结构相同的新表
    CREATE TABLE t_recored_new LIKE t_recored;
    -- 将原表的去重记录插入新表
    INSERT INTO t_recored_new SELECT DISTINCT * FROM t_recored;
    

  3. 重命名表:最后,通过重命名表,用新表替换旧表,实现无缝切换。

    RENAME TABLE t_recored TO t_recored_old, t_recored_new TO t_recored;
    

小思考:

这种“创建新表 -> 迁移数据 -> 重命名”的模式,是处理线上大表数据变更时一种相对安全和高效的策略。它避免了直接在原表上进行高风险、长时间的操作,保证了数据的完整性和服务的可用性。

9. 聚合函数

聚合函数用于对一组值进行计算,并返回单个值。它们通常与 GROUP BY 子句结合使用。

函数说明
COUNT(expr)统计行数。COUNT(*)COUNT(1) 统计所有行,COUNT(column) 则忽略该列值为 NULL 的行。
SUM(expr)计算数值列的总和,忽略 NULL 值。
AVG(expr)计算数值列的平均值,忽略 NULL 值。
MAX(expr)找出列中的最大值。
MIN(expr)找出列中的最小值。

9.1 实践一下

  • COUNT:统计有多少学生参加了英语考试(即英语成绩不为 NULL 的人数)。

    SELECT COUNT(english) FROM exam;
    

  • SUM:统计所有学生的数学成绩总分。

    SELECT SUM(math) FROM exam;
    

  • AVG:统计英语成绩的平均分。

    SELECT AVG(english) FROM exam;
    

  • MAX / MIN:同时查询数学最高分和英语最低分。

    SELECT MAX(math), MIN(english) FROM exam;
    

10. GROUP BY 分组查询

GROUP BY 子句是数据分析的利器。它将具有相同值的行分组到一起,然后我们可以对每个分组使用聚合函数进行计算。

10.1 准备实验数据

我们创建一个职员表 emp,用于演示分组查询。

CREATE TABLE emp (id BIGINT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(20) NOT NULL,role VARCHAR(20) NOT NULL,salary DECIMAL(10, 2) NOT NULL
);
INSERT INTO emp(name, role, salary) VALUES
('花味', '老板', 1500000.00), ('晓蜜', '老板', 1800000.00),
('秃头', '程序员', 10000.00), ('衬衫', '程序员', 12000.00),
('小牛', '测试', 9000.00), ('小马', '测试', 8000.00),
('孙悟空', '吉祥物', 956.8), ('猪悟能', '吉祥物', 700.5), ('沙和尚', '吉祥物', 333.3);

10.2 实践一下

  • 按角色分组,统计每个角色的人数

    SELECT role, COUNT(*) AS '人数' FROM emp GROUP BY role;
    

  • 按角色分组,统计每个角色的平均、最高和最低工资

    SELECTrole,ROUND(AVG(salary), 2) AS '平均工资',MAX(salary) AS '最高工资',MIN(salary) AS '最低工资'
    FROM emp
    GROUP BY role;
    

10.3 HAVING 子句:对分组结果进行过滤

如果我们想对 GROUP BY 之后的结果进行筛选(例如,只看平均工资大于10000的角色),就必须使用 HAVING 子句。

  • 显示平均工资大于 10000 的角色及其平均工资
    SELECTrole,ROUND(AVG(salary), 2) AS '平均工资'
    FROM emp
    GROUP BY role
    HAVING AVG(salary) > 10000;
    

10.4 HAVINGWHERE 的区别

这是一个核心知识点:

  • WHERE:在 分组前 对原始表中的行进行过滤。
  • HAVING:在 分组后GROUP BY 产生的结果集进行过滤。

简单来说:WHERE 作用于原始数据,HAVING 作用于分组后的统计结果。WHERE 子句中不能使用聚合函数,而 HAVING 子句中可以。

好了到这里MYSQL基础部分的增删改查+部分高级查询的内容结束了,希望对你有点帮助~

http://www.dtcms.com/a/289830.html

相关文章:

  • 数据结构-线性表的链式表示
  • 《P3398 仓鼠找 sugar》
  • 【1】YOLOv13 AI大模型-可视化图形用户(GUI)界面系统开发
  • 【实证分析】会计稳健性指标分析-ACF、CScore、Basu模型(2000-2023年)
  • MySQL锁(二) 共享锁与互斥锁
  • Filter快速入门 Java web
  • Compose笔记(三十七)--FilterChip
  • TVLT:无文本视觉-语言Transformer
  • c++ duiLib 显示一个简单的窗口
  • AMD处理器 5700G 矿卡RX580-8G 打英雄联盟怎么样
  • 洛谷 P10287 [GESP样题 七级] 最长不下降子序列-普及/提高-
  • 《P2680 [NOIP 2015 提高组] 运输计划》
  • 【66】MFC入门到精通——(CComboBox)下拉框选项顺序与添加顺序不一致
  • 前端静态资源免费cdn服务推荐
  • Dify极简部署手册
  • 30天打好数模基础-逻辑回归讲解
  • 7-大语言模型—指令理解:指令微调训练+模型微调
  • 【算法训练营Day15】二叉树part5
  • 编程研发工作日记
  • 050_Set接口(HashSet / TreeSet / LinkedHashSet)
  • 力扣面试150题--搜索插入位置
  • 某市公安局视频图像信息综合应用平台设计方案Word(446页)
  • AI产品经理面试宝典第40天:用户反馈处理与技术应用面试题与答法
  • 多校2+多校1的遗珠
  • 信道相关系数
  • 安装kali时出现“安装步骤失败“如何解决及后续软件安装
  • Python自动化测试项目实战
  • QT项目-仿QQ音乐的音乐播放器(第一节)
  • 什么是卡贴???
  • 国产电钢琴性价比实战选购指南