MySQL基本查询:从增删改查到复杂应用
文章目录
- 1. Create(数据插入)
- 1.1 基础插入语法
- 1.2 插入否则更新
- 1.3 替换
- 2. Retrieve(数据查询)
- 2.1 基础查询方式
- 2.2 WHERE条件查询
- 2.2.1 常用比较运算符
- 2.2.2 逻辑运算符
- 2.2.3 复杂条件案例
- 2.3 结果排序
- 2.4 筛选分页结果
- 3. Update
- 4. Delete
- 4.1 普通删除(DELETE)
- 4.2 截断表(TRUNCATE)
- 5. 插入查询结果(INSERT ... SELECT)
- 6. 聚合函数(统计分析)
- 7. GROUP BY子句(分组查询)
- 7.1 基础分组
- 8. SQL查询关键字执行顺序
本文在MySQL 5.7环境下演示:
1. Create(数据插入)
先创建基础表结构(后续案例会用到!):
-- 创建学生表
CREATE TABLE students (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
sn INT NOT NULL UNIQUE COMMENT '学号',
name VARCHAR(20) NOT NULL,
qq VARCHAR(20)
);-- 创建考试成绩表
CREATE TABLE exam_result (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL COMMENT '同学姓名',
chinese float DEFAULT 0.0 COMMENT '语文成绩',
math float DEFAULT 0.0 COMMENT '数学成绩',
english float DEFAULT 0.0 COMMENT '英语成绩'
);
1.1 基础插入语法
插入数据时,可选择全列插入或指定列插入,支持单次插入多条数据:
-- 1. 单行全列插入:value_list数量/顺序需与表列定义一致
INSERT INTO students VALUES (100, 10000, '唐三藏', NULL);
INSERT INTO students VALUES (101, 10001, '孙悟空', '11111');-- 2. 多行指定列插入:省略不插入的列(如qq),仅指定需插入列
INSERT INTO students (id, sn, name) VALUES
(102, 20001, '曹孟德'),
(103, 20002, '孙仲谋');-- 3. 插入考试成绩数据
INSERT INTO exam_result (name, chinese, math, english) VALUES
('唐三藏', 67, 98, 56),
('孙悟空', 87, 78, 77),
('猪悟能', 88, 98, 90),
('曹孟德', 82, 84, 67),
('刘玄德', 55, 85, 45),
('孙权', 70, 73, 78),
('宋公明', 75, 65, 30);
1.2 插入否则更新
当插入数据触发主键或唯一键冲突时,可选择同步更新冲突行,而非直接报错:
-- 语法:INSERT ... ON DUPLICATE KEY UPDATE
-- 案例1:主键冲突
INSERT INTO students (id, sn, name) VALUES (100, 10010, '唐大师')
ON DUPLICATE KEY UPDATE sn = 10010, name = '唐大师';
Query OK, 2 rows affected (0.00 sec)-- 案例2:唯一键冲突
-- 先创建示例表class
CREATE TABLE class (id INT PRIMARY KEY, number INT UNIQUE);
Query OK, 0 rows affected (0.02 sec)
-- 插入冲突时更新:若id=1已存在,更新number为1024
INSERT INTO class VALUES(1,32) ON DUPLICATE KEY UPDATE id=1, number=1024;
Query OK, 1 row affected (0.01 sec)-- 影响行数说明:
-- 0 row affected:冲突数据与更新值一致,无变化
-- 1 row affected:无冲突,新数据插入
-- 2 row affected:有冲突,数据已更新
-- 查看影响行数
SELECT ROW_COUNT();
1.3 替换
与“插入否则更新”不同,冲突时会先删除原有冲突行,再插入新数据:
-- 语法:REPLACE INTO ...
-- 案例1:唯一键冲突(sn=20001已存在)
REPLACE INTO students (sn, name) VALUES (20001, '曹阿瞒');
Query OK, 2 rows affected (0.00 sec)-- 案例2:
REPLACE INTO class (number) VALUES (999);
Query OK, 1 row affected (0.00 sec)-- 影响行数说明:
-- 1 row affected:无冲突,新数据插入
-- 2 row affected:有冲突,删除旧行后插入新行
2. Retrieve(数据查询)
查询是MySQL核心操作,需根据需求选择合适的查询方式,避免资源浪费。
2.1 基础查询方式
| 查询类型 | 语法示例(以exam_result为例) | 注意事项 |
|---|---|---|
| 全列查询 | SELECT * FROM exam_result; | 不建议使用:传输数据量大、可能影响索引使用 |
| 指定列查询 | SELECT id, name, english FROM exam_result; | 列顺序可与表定义不一致,仅查询需要的列 |
| 查询字段为表达式 | 1. 无字段:SELECT id, name, 10 FROM exam_result;2. 单字段: SELECT id, name, english+10 FROM exam_result;3. 多字段: SELECT id, name, chinese+math+english FROM exam_result; | 表达式可用于计算(如成绩加分、总分统计) |
| 结果指定别名 | SELECT id, name, chinese+math+english 总分 FROM exam_result;(可加AS:chinese+math+english AS 总分) | 别名仅用于查询结果显示,不能在WHERE条件中使用(因SQL执行顺序:WHERE早于SELECT) |
| 结果去重 | SELECT DISTINCT math FROM exam_result; | 去除指定列的重复值,如统计不重复的数学分数 |
2.2 WHERE条件查询
通过比较运算符和逻辑运算符,精准筛选所需数据,常用运算符如下:
2.2.1 常用比较运算符
| 运算符 | 说明 |
|---|---|
>, >=, <, <= | 大于、大于等于、小于、小于等于 |
= | 等于(NULL不安全,NULL=NULL结果为NULL) |
<=> | 等于(NULL安全,NULL<=>NULL结果为1) |
!=, <> | 不等于 |
BETWEEN a0 AND a1 | 范围匹配(左闭右闭,a0<=value<=a1) |
IN (option,...) | 匹配选项中的任意一个 |
IS NULL | 判断是否为NULL |
LIKE | 模糊匹配(%任意多字符,_任意单字符) |
2.2.2 逻辑运算符
| 运算符 | 说明 |
|---|---|
| AND | 多条件同时成立 |
| OR | 任意条件成立 |
| NOT | 否定条件 |
2.2.3 复杂条件案例
-- 1. 语文成绩好于英语成绩
SELECT name, chinese, english FROM exam_result WHERE chinese>english;
+--------+---------+---------+
| name | chinese | english |
+--------+---------+---------+
| 唐三藏 | 67 | 56 |
| 孙悟空 | 87 | 77 |
| 曹孟德 | 82 | 67 |
| 刘玄德 | 55 | 45 |
| 宋公明 | 75 | 30 |
+--------+---------+---------+
5 rows in set (0.00 sec)-- 2. 总分<200(别名不能用在WHERE,需重复表达式)
SELECT name, chinese+math+english 总分 FROM exam_result WHERE chinese+math+english<200;
+--------+------+
| name | 总分 |
+--------+------+
| 刘玄德 | 185 |
| 宋公明 | 170 |
+--------+------+
2 rows in set (0.00 sec)-- 3. 孙某 或(总分>200且语文<数学且英语>80)(括号提升优先级)
SELECT name, chinese, math, english, chinese+math+english 总分
FROM exam_result
WHERE name LIKE '孙_' OR (chinese+math+english>200 AND chinese<math AND english>80);
+--------+---------+------+---------+------+
| name | chinese | math | english | 总分 |
+--------+---------+------+---------+------+
| 猪悟能 | 88 | 98 | 90 | 276 |
| 孙权 | 70 | 73 | 78 | 221 |
+--------+---------+------+---------+------+
2 rows in set (0.00 sec)
2.3 结果排序
按指定列排序,默认升序(ASC) ,可指定降序(DESC) ,支持多列排序(优先级按顺序):
-- 语法:SELECT ... ORDER BY 列1 [ASC/DESC], 列2 [ASC/DESC]...
-- 案例1:数学升序
SELECT name, math FROM exam_result ORDER BY math;
+--------+------+
| name | math |
+--------+------+
| 宋公明 | 65 |
| 孙权 | 73 |
| 孙悟空 | 78 |
| 曹孟德 | 84 |
| 刘玄德 | 85 |
| 唐三藏 | 98 |
| 猪悟能 | 98 |
+--------+------+
7 rows in set (0.00 sec)-- 案例2:多列排序(数学降序→英语升序→语文升序)
SELECT name, math, english, chinese FROM exam_result
ORDER BY math DESC, english, chinese;
+--------+------+---------+---------+
| name | math | english | chinese |
+--------+------+---------+---------+
| 唐三藏 | 98 | 56 | 67 |
| 猪悟能 | 98 | 90 | 88 |
| 刘玄德 | 85 | 45 | 55 |
| 曹孟德 | 84 | 67 | 82 |
| 孙悟空 | 78 | 77 | 87 |
| 孙权 | 73 | 78 | 70 |
| 宋公明 | 65 | 30 | 75 |
+--------+------+---------+---------+
7 rows in set (0.00 sec)-- 案例3:按表达式排序(总分降序,支持别名)
SELECT name, chinese+math+english 总分 FROM exam_result ORDER BY 总分 DESC;
+--------+------+
| name | 总分 |
+--------+------+
| 猪悟能 | 276 |
| 孙悟空 | 242 |
| 曹孟德 | 233 |
| 唐三藏 | 221 |
| 孙权 | 221 |
| 刘玄德 | 185 |
| 宋公明 | 170 |
+--------+------+
7 rows in set (0.00 sec)-- 案例4:NULL排序(NULL视为最小,升序在最前,降序在最后)
SELECT name, qq FROM students ORDER BY qq DESC; -- qq为NULL的在最后
+--------+-------+
| name | qq |
+--------+-------+
| 孙悟空 | 11111 |
| 唐大师 | NULL |
| 孙仲谋 | NULL |
| 曹阿瞒 | NULL |
+--------+-------+
4 rows in set (0.00 sec)
2.4 筛选分页结果
用于分页查询,避免一次性查询大量数据导致数据库压力,起始下标从0开始:
-- 语法1:从0开始,取n条:LIMIT n
-- 语法2:从s开始,取n条:LIMIT s, n 或 LIMIT n OFFSET s(推荐后者,更清晰)-- 案例:exam_result表分页,每页3条(PDF案例)
-- 第1页(OFFSET 0,可省略)
SELECT id, name, math, english, chinese FROM exam_result ORDER BY id LIMIT 3 OFFSET 0;
+----+--------+------+---------+---------+
| id | name | math | english | chinese |
+----+--------+------+---------+---------+
| 1 | 唐三藏 | 98 | 56 | 67 |
| 2 | 孙悟空 | 78 | 77 | 87 |
| 3 | 猪悟能 | 98 | 90 | 88 |
+----+--------+------+---------+---------+
3 rows in set (0.00 sec)-- 第2页(从第3条开始,取3条)
SELECT id, name, math, english, chinese FROM exam_result ORDER BY id LIMIT 3 OFFSET 3;
+----+--------+------+---------+---------+
| id | name | math | english | chinese |
+----+--------+------+---------+---------+
| 4 | 曹孟德 | 84 | 67 | 82 |
| 5 | 刘玄德 | 85 | 45 | 55 |
| 6 | 孙权 | 73 | 78 | 70 |
+----+--------+------+---------+---------+
3 rows in set (0.00 sec)-- 第3页(结果不足3条时,仅返回剩余数据)
SELECT id, name, math, english, chinese FROM exam_result ORDER BY id LIMIT 3 OFFSET 6;
+----+--------+------+---------+---------+
| id | name | math | english | chinese |
+----+--------+------+---------+---------+
| 7 | 宋公明 | 65 | 30 | 75 |
+----+--------+------+---------+---------+
1 row in set (0.00 sec)-- 建议:未知表查询加LIMIT 1,避免全表查询卡死
SELECT * FROM exam_result LIMIT 1;
3. Update
修改表中已有数据,支持单列、多列更新,可结合WHERE/ORDER BY/LIMIT精准定位更新行:
-- 语法:UPDATE table_name SET 列1=值1 [, 列2=值2...] [WHERE...] [ORDER BY...] [LIMIT...]-- 案例1:单列更新(孙悟空数学改为80)
UPDATE exam_result SET math=80 WHERE name='孙悟空';-- 案例2:多列更新(曹孟德数学60、语文70)
UPDATE exam_result SET math=60, chinese=70 WHERE name='曹孟德';-- 案例3:原值基础更新(总分倒数3名数学+30)
UPDATE exam_result SET math=math+30 -- 不支持math+=30语法
ORDER BY chinese+math+english LIMIT 3;-- 案例4:全表更新(慎用!所有同学语文×2)
UPDATE exam_result SET chinese=chinese*2;
4. Delete
删除表中数据,支持按条件删除,也可删除全表,但需注意与“截断表”的区别。
4.1 普通删除(DELETE)
-- 语法:DELETE FROM table_name [WHERE...] [ORDER BY...] [LIMIT...]-- 案例1:按条件删除(删除孙悟空成绩)
DELETE FROM exam_result WHERE name='孙悟空';-- 案例2:删除全表数据(慎用!AUTO_INCREMENT不重置)
-- 先创建测试表
CREATE TABLE for_delete (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
INSERT INTO for_delete (name) VALUES ('A'),('B'),('C');
-- 删除全表
DELETE FROM for_delete;
-- 插入新数据:id从4开始(原自增到3,未重置)
INSERT INTO for_delete (name) VALUES ('D'); -- id=4
4.2 截断表(TRUNCATE)
与DELETE全表删除的区别:TRUNCATE更快、无法回滚、重置AUTO_INCREMENT:
-- 语法:TRUNCATE [TABLE] table_name-- 案例
-- 先创建测试表
CREATE TABLE for_truncate (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
INSERT INTO for_truncate (name) VALUES ('A'),('B'),('C');
-- 截断表
TRUNCATE for_truncate;
-- 插入新数据:id从1开始(自增重置)
INSERT INTO for_truncate (name) VALUES ('D'); -- id=1-- 注意事项:
-- 1. 仅能全表操作,不能按条件删除
-- 2. 不经过事务,无法回滚(DELETE支持回滚)
-- 3. 重置AUTO_INCREMENT为初始值
5. 插入查询结果(INSERT … SELECT)
将一个查询的结果直接插入到另一张表,常用于数据迁移或去重:
-- 语法:INSERT INTO 目标表 [(列1,...)] SELECT ... FROM 源表...-- 案例:删除表中重复记录
-- 1. 创建与源表结构一致的空表(笔记补充:IF NOT EXISTS避免表已存在报错)
CREATE TABLE IF NOT EXISTS no_duplicate_table LIKE duplicate_table;-- 2. 源表去重后插入新表
INSERT INTO no_duplicate_table SELECT DISTINCT * FROM duplicate_table;-- 3. 重命名表,实现原子去重
RENAME TABLE duplicate_table TO old_duplicate_table,
no_duplicate_table TO duplicate_table;-- 4. 笔记补充:也可通过ALTER重命名(与PDF语法等效)
ALTER TABLE copy RENAME TO exam_result;
6. 聚合函数(统计分析)
对数据进行统计计算,常用函数如下(PDF表格+笔记补充案例):
| 函数 | 说明 | 案例(基于exam_result/students表) |
|---|---|---|
| COUNT([DISTINCT] expr) | 统计数据行数(expr为*时含NULL,为列时不含NULL) | 1. 学生总数:SELECT COUNT(*) FROM students;2. 有qq的学生数: SELECT COUNT(qq) FROM students;3. 不重复数学分数个数: SELECT COUNT(DISTINCT math) FROM exam_result; |
| SUM([DISTINCT] expr) | 求和(仅数字有效) | 1. 数学总分:SELECT SUM(math) FROM exam_result;2. 数学及格总分: SELECT SUM(math) FROM exam_result WHERE math>=60; |
| AVG([DISTINCT] expr) | 求平均(仅数字有效) | 1. 英语平均分:SELECT AVG(english) FROM exam_result;2. 笔记补充:自定义平均(总分/人数): SELECT SUM(chinese+math+english)/COUNT(*) 平均总分 FROM exam_result; |
| MAX([DISTINCT] expr) | 求最大值 | 英语最高分:SELECT MAX(english) FROM exam_result; |
| MIN([DISTINCT] expr) | 求最小值 | 数学>70的最低分:SELECT MIN(math) FROM exam_result WHERE math>70; |
– 注意:无符合条件的数据时,SUM/AVG/MAX/MIN返回NULL,COUNT返回0
7. GROUP BY子句(分组查询)
按指定列分组,对每组进行统计分析,SELECT后仅能出现分组列和聚合函数。
7.1 基础分组
先创建EMP员工表:
-- 简化版EMP表
CREATE TABLE `emp` (`empno` int(4) NOT NULL,`ename` varchar(10) DEFAULT NULL,`job` varchar(9) DEFAULT NULL,`mgr` int(4) DEFAULT NULL,`hiredate` date DEFAULT NULL,`sal` decimal(7,2) DEFAULT NULL,`comm` decimal(7,2) DEFAULT NULL,`deptno` int(2) DEFAULT NULL,PRIMARY KEY (`empno`),KEY `fk_emp_dept` (`deptno`),CONSTRAINT `fk_emp_dept` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 简化版dept表
CREATE TABLE `dept` (`deptno` int(2) NOT NULL,`dname` varchar(14) DEFAULT NULL,`loc` varchar(13) DEFAULT NULL,PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 插入测试数据(示例)
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800.00, NULL, 20),
(7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600.00, 300.00, 30),
(7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30),
(7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975.00, NULL, 20),
(7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30),
(7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850.00, NULL, 30),
(7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450.00, NULL, 10),
(7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000.00, NULL, 20),
(7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000.00, NULL, 10),
(7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500.00, 0.00, 30),
(7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100.00, NULL, 20),
(7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950.00, NULL, 30),
(7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000.00, NULL, 20),
(7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300.00, NULL, 10);
分组查询案例:
-- 案例1:按部门分组,查每个部门的平均工资和最高工资
SELECT deptno, AVG(sal) 平均工资, MAX(sal) 最高工资 FROM EMP GROUP BY deptno;
+--------+-------------+----------+
| deptno | 平均工资 | 最高工资 |
+--------+-------------+----------+
| 10 | 2916.666667 | 5000.00 |
| 20 | 2175.000000 | 3000.00 |
| 30 | 1566.666667 | 2850.00 |
+--------+-------------+----------+
3 rows in set (0.01 sec)-- 案例2:按部门+岗位分组,查每个部门各岗位的平均工资和最低工资
SELECT deptno, job, AVG(sal) 平均工资, MIN(sal) 最低工资 FROM EMP GROUP BY deptno, job;
+--------+-----------+-------------+----------+
| deptno | job | 平均工资 | 最低工资 |
+--------+-----------+-------------+----------+
| 10 | CLERK | 1300.000000 | 1300.00 |
| 10 | MANAGER | 2450.000000 | 2450.00 |
| 10 | PRESIDENT | 5000.000000 | 5000.00 |
| 20 | ANALYST | 3000.000000 | 3000.00 |
| 20 | CLERK | 950.000000 | 800.00 |
| 20 | MANAGER | 2975.000000 | 2975.00 |
| 30 | CLERK | 950.000000 | 950.00 |
| 30 | MANAGER | 2850.000000 | 2850.00 |
| 30 | SALESMAN | 1400.000000 | 1250.00 |
+--------+-----------+-------------+----------+
9 rows in set (0.00 sec)-- 案例3:分组后筛选(HAVING,过滤平均工资<2000的部门)
SELECT deptno, AVG(sal) 平均工资 FROM EMP GROUP BY deptno HAVING 平均工资<2000;
+--------+-------------+
| deptno | 平均工资 |
+--------+-------------+
| 30 | 1566.666667 |
+--------+-------------+
1 row in set (0.00 sec)-- 案例4:WHERE+GROUP BY+HAVING(先过滤员工,再分组筛选)
-- 排除empno=7566的员工后,查平均工资<2000的部门
SELECT deptno, AVG(sal) 平均工资 FROM EMP WHERE empno!=7566 GROUP BY deptno HAVING 平均工资<2000;
+--------+-------------+
| deptno | 平均工资 |
+--------+-------------+
| 20 | 1975.000000 |
| 30 | 1566.666667 |
+--------+-------------+
2 rows in set (0.00 sec)
8. SQL查询关键字执行顺序
查询语句中各关键字的执行顺序决定了语法逻辑:
FROM → ON → JOIN → WHERE → GROUP BY → WITH → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
WHERE早于GROUP BY:WHERE用于分组前过滤数据,HAVING用于分组后过滤结果;SELECT晚于WHERE/GROUP BY:别名在SELECT中定义,因此不能在WHERE/GROUP BY中使用,可在HAVING/ORDER BY中使用;DISTINCT晚于SELECT:先查询出结果,再去重;LIMIT最后执行:仅对排序后的结果分页。
