MySQL 前三周
MySQL
第一周
一、数据库连接与基础操作
连接 MySQL
1
mysql -u root -p # 输入密码后进入命令行
- 注意:
-u后接用户名,-p表示需要密码(输入后无回显)
- 注意:
退出 MySQL
1
EXIT; -- 或使用 \q、QUIT
二、数据库操作
查看所有数据库
1
SHOW DATABASES; -- 显示所有数据库列表
创建数据库
1
CREATE DATABASE mydb; -- 数据库名需唯一,区分大小写(取决于系统配置)
删除数据库
1
DROP DATABASE mydb; -- 慎用!会永久删除数据库及数据
切换数据库
1
USE mydb; -- 后续操作默认在此数据库执行
三、表操作
创建表
1
2
3
4
5CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY, -- 自增主键(必须为主键或唯一索引)
name VARCHAR(50) NOT NULL, -- VARCHAR需指定长度,NOT NULL约束
age INT NOT NULL
);- 易错点:
AUTO_INCREMENT必须与主键/唯一索引搭配使用。VARCHAR(50)必须指定长度,超出部分会被截断。
- 易错点:
查看表结构
1
DESC users; -- 显示字段名、类型、约束等元信息
删除表
1
DROP TABLE users; -- 慎用!删除表及所有数据
查看所有表
1
SHOW TABLES; -- 需先 USE 数据库
四、数据操作(CRUD)
插入数据
1
INSERT INTO users (name, age) VALUES ('Alice', 25), ('Bob', 30); -- 多值插入
- 注意:
- 字段顺序需与 VALUES 中值顺序一致。
- 自增字段(如
id)无需手动插入。
- 注意:
查询数据
1
SELECT * FROM users; -- 查询所有字段
- 易错点:
*可能导致查询效率低下,建议明确指定字段。
- 易错点:
更新数据
1
UPDATE users SET age = 26 WHERE name = 'Alice'; -- WHERE 条件必须明确!
- 重点强调:
- 无
WHERE条件的UPDATE会更新全表数据! - 字符串条件需用单引号(
'Alice')。
- 无
- 重点强调:
删除数据
1
DELETE FROM users WHERE name = 'Bob'; -- WHERE 条件必须明确!
- 重点强调:
- 无
WHERE条件的DELETE会清空整张表! - 与
DROP TABLE不同,DELETE只删数据,不删表结构。
- 无
- 重点强调:
五、易混淆知识点
DROPvsDELETEvsTRUNCATEDROP TABLE: 删除表结构及数据。DELETE FROM: 删除数据,保留表结构(可回滚)。TRUNCATE TABLE: 快速清空表数据(不可回滚,重置自增值)。
CHARvsVARCHARCHAR(50): 固定长度,不足补空格,适合短且长度固定的数据(如邮编)。VARCHAR(50): 可变长度,按实际长度存储,适合长度不定的数据(如用户名)。
AUTO_INCREMENT使用限制- 每个表只能有一个自增列。
- 需为整数类型(如
INT,BIGINT)。
第二周
一、基础查询语法
SELECT 基本结构
1
SELECT 列名1, 列名2 FROM 表名 [WHERE 条件];
- 示例:
select ID,name,salary from instructor; - 注意:
SELECT *会返回所有列,可能影响性能(需谨慎使用)
- 示例:
WHERE 条件过滤
1
WHERE dept_name='Physics'
- 易错点:字符串值必须用单引号
'',如'Physics'(错误示例:'Pyhsics'导致空结果)。 - 注意:条件值大小写敏感(如
'Fall'vs'FALL'),具体取决于数据库配置。
- 易错点:字符串值必须用单引号
二、UNION 操作符
- 合并查询结果
1
SELECT ... UNION SELECT ...
- 示例:联合两个学期的课程 ID。
- 易错点:
UNION自动去重,UNION ALL保留重复记录。 - 相似点:
UNION要求列数/类型一致,而JOIN用于横向连接表。
三、DDL(数据定义语言)
创建表
1
2
3
4
5CREATE TABLE 表名 (
列名 数据类型 [约束],
PRIMARY KEY(列名),
FOREIGN KEY(列名) REFERENCES 被引用表(列名)
);- 示例:
create table sc(...)。 - 易错点:外键需引用已存在的表和列,否则报错。
- 示例:
修改表结构
1
ALTER TABLE 表名 ADD 列名 数据类型 [约束];
- 示例:
alter table student add sex varchar(50) not null; - 易错点:添加非空列时,若表中已有数据,需指定默认值或分步操作。
- 示例:
四、约束
主键约束
1
PRIMARY KEY(列名)
- 唯一且非空,如
primary key(stuid)。
- 唯一且非空,如
外键约束
1
FOREIGN KEY(列名) REFERENCES 目标表(目标列)
- 确保引用完整性,如
foreign key(cid) references course(cid)。
- 确保引用完整性,如
非空约束
1
NOT NULL
- 强制列不允许空值,如
sex varchar(50) not null。
- 强制列不允许空值,如
五、数据库管理
数据库操作
1
2
3CREATE DATABASE 数据库名;
DROP DATABASE 数据库名;
USE 数据库名;- 示例:
create database test;
- 示例:
查看元数据
1
2SHOW TABLES; -- 查看所有表
DESC 表名; -- 查看表结构
六、易混淆知识点
UNIONvsJOINUNION纵向合并结果集,JOIN横向关联表。
PRIMARY KEYvsUNIQUE- 主键隐含非空,唯一约束允许空值(除非显式添加
NOT NULL)。
- 主键隐含非空,唯一约束允许空值(除非显式添加
WHEREvsHAVINGWHERE过滤行,HAVING过滤分组(通常与GROUP BY配合)。
七、总结
- 重点注意:
- 字符串条件值的引号与拼写正确性。
UNION去重特性与JOIN的关联逻辑。- 外键约束的引用完整性。
- 添加非空列的潜在风险。
- 区分大小写敏感的场景(如条件值、表名/列名)。
第三周
1. 修改表结构
语法:
1 | ALTER TABLE 表名 ADD 列名 数据类型 [约束]; |
示例:
1 | ALTER TABLE student ADD sex VARCHAR(50) NOT NULL; -- 添加非空列 |
注意:
- 添加非空列时,若表中已有数据,需指定默认值或分步操作(先添加列再更新数据)。
- 数据类型需明确(如
VARCHAR(50)、INT)。
2. 基础查询
语法:
1 | SELECT 列名1, 列名2 FROM 表名 [WHERE 条件]; |
示例:
1 | SELECT name, salary FROM instructor WHERE salary > 80000; |
WHERE条件中字符串需用单引号(如dept_name='Comp. Sci.')
字段名歧义时需指定表名(如instructor.dept_name)
计算字段值
SELECT column_name, expression AS alias_name FROM table_name;计算并为新列命名。示例: 计算教师的月薪:
1
SELECT dept_name, salary/12 AS month_salary FROM instructor;
其中
salary/12计算月薪,AS month_salary为其取别名,取别名(alias)不会存储在数据库中,而只是用于当前查询的结果集
3. 范围与模糊查询
语法:
1 | WHERE 列名 BETWEEN 值1 AND 值2; -- 闭区间范围 |
示例:
1 | WHERE salary BETWEEN 60000 AND 80000; -- 包含 60000 和 80000 |
NOT BETWEEN表示范围外LIKE区分大小写(取决于数据库配置)
4. 多表连接
语法:
1 | SELECT 表1.列名, 表2.列名 |
示例:
1 | SELECT name, course_id |
多表字段名冲突需明确表名(如
course.course_id)
推荐使用显式JOIN语法提高可读性。
WHERE instructor.id = teaches.id
这部分的作用是 连接 instructor 和 teaches 表,即匹配教师和他们教授的课程。
instructor.id是instructor表中的教师编号。teaches.id是teaches表中表示授课教师的编号。- 这个条件确保
teaches表的记录和instructor表中的教师对应起来
AND teaches.course_id = course.course_id
这个条件连接 teaches 和 course 表,确保授课记录中的课程编号和 course 表中的课程编号匹配,即找出每位教师所教授的课程的详细信息
AND instructor.dept_name = ‘Comp. Sci.’
只筛选 instructor 表中 dept_name 为 'Comp. Sci.'(计算机科学系)的教师
如果不加这些条件,查询结果会出现 笛卡尔积(Cartesian Product),即
instructor和teaches、course之间的所有组合,数据会混乱
简单的例子
假设我们有以下三张表
教师表 (instructor)
| id | name | dept_name |
|---|---|---|
| 101 | Alice | Comp. Sci. |
| 102 | Bob | Comp. Sci. |
| 103 | Charlie | Math |
授课表 (teaches)
| id | course_id |
|---|---|
| 101 | CS101 |
| 101 | CS102 |
| 102 | CS103 |
| 103 | MATH101 |
课程表 (course)
| course_id | title |
|---|---|
| CS101 | Data Structures |
| CS102 | Algorithms |
| CS103 | Operating Systems |
| MATH101 | Calculus |
在
teaches表中,id不是主键,而id + course_id的组合才可能是主键。让我们详细分析这个问题
逐步执行:
instructor.id = teaches.id这一步连接
instructor和teaches,得到:id name dept_name id course_id 101 Alice Comp. Sci. 101 CS101 101 Alice Comp. Sci. 101 CS102 102 Bob Comp. Sci. 102 CS103 103 Charlie Math 103 MATH101 Charlie 还在
AND instructor.dept_name = 'Comp. Sci.'这一步过滤掉 不属于
'Comp. Sci.'的教师,即103 (Charlie)被排除,剩下:id name dept_name id course_id 101 Alice Comp. Sci. 101 CS101 101 Alice Comp. Sci. 101 CS102 102 Bob Comp. Sci. 102 CS103
AND teaches.course_id = course.course_id连接
course表,获取课程名称:name course_id title Alice CS101 Data Structures Alice CS102 Algorithms Bob CS103 Operating Systems
5. 聚合与分组
语法:
1 | SELECT 聚合函数(列名) FROM 表名 [GROUP BY 列名] [HAVING 条件]; |
示例:
1 | SELECT SUM(salary) AS total_salary, AVG(salary) AS avg_salary FROM instructor; |
COUNT(*)统计所有行,COUNT(列名)忽略 NULLHAVING必须与GROUP BY配合使用
第一条 SQL 语句
1 | SELECT SUM(salary) AS total_salary, AVG(salary) AS avg_salary FROM instructor; |
作用:
计算 instructor 表中所有教师的工资总和 (SUM(salary)) 和平均工资 (AVG(salary))。
执行流程:
- SUM(salary): 计算
salary列所有教师工资的总和。 - AVG(salary): 计算
salary列所有教师工资的平均值。 - AS total_salary 和 AS avg_salary: 仅用于将结果列重命名,方便阅读。
示例数据(instructor 表)
| id | name | dept_name | salary |
|---|---|---|---|
| 101 | Alice | Comp. Sci. | 70000 |
| 102 | Bob | Comp. Sci. | 60000 |
| 103 | Charlie | Math | 80000 |
计算:
- SUM(salary) = 70000 + 60000 + 80000 = 210000
- AVG(salary) = (70000 + 60000 + 80000) / 3 = 70000
返回结果
| total_salary | avg_salary |
|---|---|
| 210000 | 70000 |
第二条 SQL 语句
1 | SELECT dept_name, COUNT(*) FROM instructor GROUP BY dept_name; |
作用:
统计 instructor 表中 每个系 的教师人数。
执行流程:
- GROUP BY dept_name: 按
dept_name(系名)分组,每个组包含该系的所有教师。 - COUNT(*): 计算每个
dept_name组内的行数(即该系的教师总数)。
示例数据
| id | name | dept_name | salary |
|---|---|---|---|
| 101 | Alice | Comp. Sci. | 70000 |
| 102 | Bob | Comp. Sci. | 60000 |
| 103 | Charlie | Math | 80000 |
| 104 | David | Math | 65000 |
按照 dept_name 分组:
- Comp. Sci. 组:Alice, Bob → 2人
- Math 组:Charlie, David → 2人
返回结果
| dept_name | COUNT(*) |
|---|---|
| Comp. Sci. | 2 |
| Math | 2 |
第三条 SQL 语句
1 | SELECT dept_name, AVG(salary) |
作用:
统计 平均工资高于 60000 的系,并返回系名和该系的平均工资。
执行流程:
- GROUP BY dept_name: 按
dept_name(系名)分组,每个组包含该系的所有教师。 - AVG(salary): 计算每个
dept_name组的平均工资。 - HAVING AVG(salary) > 60000: 过滤掉平均工资 ≤ 60000 的系,只保留
AVG(salary) > 60000的系。
示例数据
| id | name | dept_name | salary |
|---|---|---|---|
| 101 | Alice | Comp. Sci. | 70000 |
| 102 | Bob | Comp. Sci. | 60000 |
| 103 | Charlie | Math | 80000 |
| 104 | David | Math | 65000 |
计算 AVG(salary):
- Comp. Sci.: (70000 + 60000) / 2 = 65000
- Math: (80000 + 65000) / 2 = 72500
HAVING 过滤:
- Comp. Sci.: 65000 ✅(保留)
- Math: 72500 ✅(保留)
返回结果
| dept_name | AVG(salary) |
|---|---|
| Comp. Sci. | 65000 |
| Math | 72500 |
6. 子查询
语法:
1 | WHERE 列名 比较运算符 (子查询); |
示例:
1 | -- 工资高于 Comp. Sci. 部门最低工资的教师 |
1 | -- 选修了所有 Biology 课程的学生 |
SOME/ANY等价于比较子查询的最小值ALL等价于比较子查询的最大值。
第一条 SQL 语句
1 | -- 工资高于 Comp. Sci. 部门最低工资的教师 |
作用:
查询 instructor 表中工资 高于 Comp. Sci. 部门最低工资 的所有教师的名字。
执行流程
子查询:
1
(SELECT MIN(salary) FROM instructor WHERE dept_name = 'Comp. Sci.')
- 先在
instructor表中过滤出Comp. Sci.部门的教师。 - 然后计算该部门的 最低工资(
MIN(salary))。
- 先在
外查询:
1
SELECT name FROM instructor WHERE salary > (子查询结果);
- 在
instructor表中查找所有 工资高于子查询计算出的最低工资 的教师,并返回他们的name。
- 在
示例数据
| id | name | dept_name | salary |
|---|---|---|---|
| 101 | Alice | Comp. Sci. | 70000 |
| 102 | Bob | Comp. Sci. | 60000 |
| 103 | Charlie | Math | 80000 |
| 104 | David | Physics | 75000 |
子查询计算
- Comp. Sci. 部门有:
- Alice(70000)
- Bob(60000)
- MIN(salary) = 60000(Bob 的工资)
外查询筛选
找出 salary > 60000 的教师:
- Alice (70000) ✅(符合)
- Charlie (80000) ✅(符合)
- David (75000) ✅(符合)
- Bob (60000) ❌(不符合)
返回结果
| name |
|---|
| Alice |
| Charlie |
| David |
第二条 SQL 语句
1 | -- 选修了所有 Biology 课程的学生 |
作用:
查询 选修了 Biology 系所有课程 的学生。
执行流程
子查询 1(course 表):
1
2SELECT * FROM course AS C
WHERE C.dept_name = 'Biology'- 选出所有 Biology 系 的课程。
子查询 2(NOT EXISTS 检查):
1
2
3
4NOT EXISTS (
SELECT * FROM takes
WHERE takes.id = S.id AND takes.course_id = C.course_id
)- 这里的
NOT EXISTS作用是:- 检查
student是否 没有 选修 Biology 课程C.course_id。 - 若
NOT EXISTS为TRUE,表示该学生没有选修某个 Biology 课程,则该学生被排除。
- 检查
- 这里的
外层 NOT EXISTS:
1
WHERE NOT EXISTS ( ... )
- 只有当
student选修了 所有 Biology 课程 时,NOT EXISTS结果才是FALSE,该student才会被保留。
- 只有当
示例数据
student 表
| ID | name |
|---|---|
| 1 | Tom |
| 2 | Jerry |
| 3 | Alice |
course 表(Biology 课程)
| course_id | dept_name |
|---|---|
| BIO101 | Biology |
| BIO102 | Biology |
takes 表(选课情况)
| id | course_id |
|---|---|
| 1 | BIO101 |
| 1 | BIO102 |
| 2 | BIO101 |
| 3 | BIO101 |
| 3 | BIO102 |
执行过程
WHERE C.dept_name = ‘Biology’:
- Biology 课程:
BIO101,BIO102。
- Biology 课程:
NOT EXISTS (SELECT * FROM takes WHERE takes.id = S.id AND takes.course_id = C.course_id):
- 对于每个学生检查是否有 Biology 课程未选修:
- Tom(ID = 1):
- 选了
BIO101和BIO102✅(没有 Biology 课程遗漏) NOT EXISTS = FALSE(不遗漏)NOT EXISTS(...) = FALSE,所以 Tom 被 保留。
- 选了
- Jerry(ID = 2):
- 选了
BIO101,但没选BIO102❌ NOT EXISTS = TRUE(有遗漏)NOT EXISTS(...) = TRUE,所以 Jerry 被 排除。
- 选了
- Alice(ID = 3):
- 选了
BIO101和BIO102✅(没有 Biology 课程遗漏) NOT EXISTS = FALSE(不遗漏)NOT EXISTS(...) = FALSE,所以 Alice 被 保留。
- 选了
- Tom(ID = 1):
- 对于每个学生检查是否有 Biology 课程未选修:
最终结果
| ID | name |
|---|---|
| 1 | Tom |
| 3 | Alice |
7. 数据插入与外键约束
语法:
1 | INSERT INTO 表名 (列1, 列2) VALUES (值1, 值2); |
示例:
1 | -- 正确插入(需先满足外键引用) |
外键字段必须引用已存在的值
插入顺序应为父表 → 子表
在数据库中,外键(Foreign Key) 是用来确保数据的 完整性和一致性 的。它规定了某个表中的字段 必须在另一个表中存在,否则插入或更新数据时就会发生外键冲突
一句话,先有鸡后有蛋,父亲才能生儿子,儿子不能生父亲
假设有两张表:
department(部门表):
1 | CREATE TABLE department ( |
instructor(教师表),其中dept_name是外键,引用department表:
1 | CREATE TABLE instructor ( |
✔ 正确插入
1 | INSERT INTO department VALUES ('Comp. Sci.', 100000); -- 先插入部门 |
🔴 错误插入
1 | INSERT INTO instructor VALUES (102, 'Bob', 'Math'); -- 错误!'Math' 部门不存在于 department 表中,导致外键冲突 |
总结
- 通用规则:
- 字段名歧义时需指定表名(如
表名.列名)。 - 字符串用单引号,数值无需引号。
- 外键操作需确保引用完整性。
- 字段名歧义时需指定表名(如
- 易错点:
WHERE dept_name = NULL应改为WHERE dept_name IS NULL。- 多表连接未指定条件会导致笛卡尔积。
- 优化建议:
- 使用显式
JOIN代替隐式连接。 - 避免
SELECT *,明确指定字段。
- 使用显式


