大学计算机专业 · 数据库原理与应用(MySQL 8.x)

SQL 与数据库:
SELECT 查询到综合项目实战

本教程面向大学一年级学生,按"基础概念 → SQL 语句 → 查询 → 多表 → 数据库设计 → MySQL 实践 → 索引事务 → 数据库编程 → 综合项目"的路线展开。重点突出SELECT 查询、JOIN 多表查询、数据库设计、索引优化—— 这是每个开发者的核心技能

关系数据库用表来组织数据
SQL 标准ANSI / ISO 通用查询语言
MySQL / PostgreSQL主流开源数据库
JDBC / PyMySQL数据库编程接口
↓ 向下滚动,按章节系统学习并完成课堂练习(第 08-13 章是 SELECT 核心)
00

学习指南:为什么学、怎么学、学到什么

在动手写 SQL 之前,先建立对"数据库"和"SQL"的基本认知。

0.1 什么是数据库

数据库(Database)长期存储在计算机内的、有组织的、可共享的数据集合

  • DBMS(数据库管理系统):管理数据库的软件,如 MySQL、PostgreSQL;
  • 数据库系统 = 数据库 + DBMS + 应用程序 + 数据库管理员;
  • 数据库服务器:运行 DBMS 的进程,对外提供数据库服务;
  • 数据库应用程序:使用数据库的业务系统(如学生管理系统)。

为什么需要数据库

  • 解决文件存储的问题:并发访问、备份恢复、数据一致性;
  • 减少数据冗余:数据集中管理,避免重复;
  • 保证数据一致性:约束 + 事务;
  • 数据共享:多用户、多应用同时访问;
  • 数据安全:权限控制、加密、审计;
  • 高性能查询:索引、查询优化器。

一句话总结:数据库是几乎所有软件系统的核心 —— 学好 SQL,就是掌握打开数据世界大门的钥匙。

0.2 SQL 是什么

SQL(Structured Query Language)是用于操作关系数据库的标准编程语言。

  • SQL ≠ MySQL:SQL 是标准,MySQL 是实现该标准的一个具体数据库;
  • SQL 标准:ANSI / ISO 维护,多个版本(SQL-92、SQL:1999、SQL:2003、SQL:2008 等);
  • 方言:MySQL、PostgreSQL、Oracle、SQL Server 都遵循 SQL 标准,但各有"方言"扩展。

主流数据库产品

  • MySQL:开源、免费、Web 主流(教学首选);
  • PostgreSQL:功能更强的高级开源数据库;
  • SQLite:嵌入式文件型数据库(Python 内置);
  • Oracle:商业旗舰,企业级常用;
  • SQL Server:微软产品,与 .NET 生态深度结合。

0.3 学习路线(按 SQL 大纲)

数据库基础第 01-04 章
SQL 查询第 05-12 章 ⭐⭐
多表 / JOIN第 13-14 章 ⭐
数据库设计第 15-16 章
MySQL 实践第 17 章
索引 / 事务第 18-20 章
数据库编程第 22-23 章
综合项目第 24 章

三档学习目标

  • 必须掌握 ⭐⭐⭐:数据库概念 → 表 → 主键/外键 → CRUD → SELECT → WHERE → ORDER BY → GROUP BY → 聚合函数 → JOIN → 数据库设计 → MySQL
  • 应该掌握 ⭐⭐:索引 → 事务 → ACID → 视图 → 窗口函数 → Python/Java 数据库编程
  • 拓展 :存储过程 → 触发器 → B+树 → SQL 注入 → 分布式数据库
01

数据库基本概念 ⭐⭐⭐

本节介绍数据库中最常用的"词汇" —— 理解它们能让你后续学习事半功倍。

1.1 数据库中的基本对象

术语含义类比
数据库 (Database)一组相关表的集合一个 Excel 工作簿
表 (Table)二维数据结构,行 + 列工作簿中一个 sheet
行 (Row) / 记录 (Record)表中的一行数据Excel 一行
列 (Column) / 字段 (Field)表中的一列Excel 一列
主键 (Primary Key)唯一标识一行学号、身份证号
外键 (Foreign Key)关联另一张表的主键成绩表的"学号"

1.2 常用数据类型

类别类型说明
数值INT, BIGINT, DECIMAL, FLOAT, DOUBLE整数 / 小数
字符串CHAR, VARCHAR, TEXTCHAR 定长、VARCHAR 变长
日期时间DATE, TIME, DATETIME, TIMESTAMPYYYY-MM-DD 等
布尔BOOLEAN / TINYINT(1)真 / 假
特殊NULL"无值",与 0 / '' 不同

1.3 主键 Primary Key ⭐⭐⭐

  • 唯一:每行主键值都不相同;
  • 非空:主键列不允许 NULL;
  • 每张表只能有一个主键(可以是复合主键);
  • 自增主键:MySQL 中 INT AUTO_INCREMENT PRIMARY KEY,由数据库自动生成。

1.4 表之间的三种关系 ⭐⭐

用户 1 : 1 身份证 一对一:每个用户只有一张身份证 班级 1 : N 学生 一对多:一个班级有多个学生 学生 M : N 课程 多对多:一个学生选多门课, 一门课被多个学生选 → 通常拆成"中间表"
数据库表之间的三种关系
02

SQL 语句分类 ⭐⭐⭐

SQL 语句按功能可分为五类。掌握分类后,看到任何 SQL 都能立刻知道它的"作用"。

SQL 五大类语句

类型含义主要语句举例
DDL数据定义语言CREATE / ALTER / DROP建表、改表、删表
DML数据操作语言INSERT / UPDATE / DELETE增、改、删数据
DQL数据查询语言SELECT查询数据
DCL数据控制语言GRANT / REVOKE权限管理
TCL事务控制语言COMMIT / ROLLBACK事务提交与回滚

学习优先级:大一阶段重点掌握 DQL(SELECT)、DML(CRUD)、DDL —— 这三类覆盖了 95% 的日常工作。

03

数据库操作 ⭐⭐⭐

数据库 = 多个表的"容器"。本节学如何创建、查看、使用、删除数据库。

3.1 四大操作

SQLdb_ops.sql
-- 创建数据库(指定字符集)
CREATE DATABASE school DEFAULT CHARACTER SET utf8mb4;

-- 查看所有数据库
SHOW DATABASES;

-- 切换到目标数据库
USE school;

-- 删除数据库(⚠️ 高危操作)
DROP DATABASE school;
DROP DATABASE 是高危操作:它会删除整个数据库及其下所有表与数据,无法恢复。生产环境务必先备份再执行。
04

数据表操作 ⭐⭐⭐

表是数据库的核心。本节学 CREATE / ALTER / DROP 表。

4.1 CREATE TABLE

SQLcreate_table.sql
CREATE TABLE student (
    id          INT          PRIMARY KEY AUTO_INCREMENT,
    name        VARCHAR(50)  NOT NULL,
    age         INT          CHECK (age >= 0 AND age <= 150),
    gender      VARCHAR(10)  DEFAULT '未知',
    email       VARCHAR(100) UNIQUE,
    created_at  DATETIME     DEFAULT CURRENT_TIMESTAMP
);

-- 查看表结构
DESC student;

-- 查看建表 SQL
SHOW CREATE TABLE student;
命名规范:表名/列名小写 + 下划线(student_name);表名复数(studentsorders);避免 SQL 关键字。

4.2 ALTER TABLE:修改表

SQLalter_table.sql
-- 添加列
ALTER TABLE student ADD phone VARCHAR(20);

-- 修改列类型
ALTER TABLE student MODIFY name VARCHAR(100);

-- 重命名列
ALTER TABLE student CHANGE phone mobile VARCHAR(20);

-- 删除列
ALTER TABLE student DROP COLUMN mobile;

-- 重命名表
ALTER TABLE student RENAME TO students;

-- 删除表
DROP TABLE student;
05

INSERT 插入数据 ⭐⭐⭐

DML 三大操作之一:

5.1 单条 / 多条 / 指定字段插入

SQLinsert.sql
-- 单条插入(推荐:指定字段名)
INSERT INTO student (name, age, gender)
VALUES ('张三', 18, '男');

-- 多条插入
INSERT INTO student (name, age, gender)
VALUES
    ('李四', 19, '女'),
    ('王五', 20, '男'),
    ('赵六', 21, '女');

-- 从另一张表复制
INSERT INTO student_backup
SELECT * FROM student WHERE age >= 18;
最佳实践:永远指定列名插入 —— 即使是所有列。这样表结构变更时不会出错。
06

UPDATE 修改数据 ⭐⭐⭐

DML 第二大操作:UPDATE 必带 WHERE 是铁律。

6.1 UPDATE + WHERE

SQLupdate.sql
-- ✔ 正确:带 WHERE 条件
UPDATE student
SET age = 19
WHERE id = 1;

-- 多字段更新
UPDATE student
SET age = 20, gender = '女'
WHERE name = '李四';

-- ❌ 灾难性错误:忘记 WHERE
-- UPDATE student SET age = 20;
-- → 会把所有学生的年龄都改成 20!
UPDATE 没有 WHERE 子句 = 整张表被更新。这是最常见的"生产事故"。建议执行前先 SELECT ... WHERE ... 验证条件。
07

DELETE 删除数据 ⭐⭐⭐

DML 第三大操作:。三种"删"的区别是高频考点。

7.1 DELETE / DROP / TRUNCATE 区别

操作对象回滚?自增?速度
DELETE FROM t WHERE ...数据✔ 可回滚保留
TRUNCATE TABLE t数据❌ 不能回滚重置
DROP TABLE t❌ 不能回滚删除表本身最快
SQLdelete.sql
-- ✔ 删除指定行
DELETE FROM student WHERE id = 1;

-- 慎用:清空整张表
TRUNCATE TABLE student;

-- 慎用:删除整张表
DROP TABLE student;

课堂练习 · 第03-07章 DDL/DML

1CREATE TABLE 属于 SQL 五大类的哪一类?
2关于 TRUNCATE TABLE 说法正确的是?
3UPDATE student SET age = 20 没带 WHERE 会怎样?
08

SELECT 查询 ⭐⭐⭐ —— SQL 真正的核心

SELECT 是 SQL 中最重要、使用频率最高的语句。整个数据分析、报表、业务功能都依赖它。

8.1 SELECT 语法骨架

SQLselect_skeleton.sql
SELECT    [DISTINCT]  列1, 列2 AS 别名, 聚合函数(列)
FROM      表名
WHERE     行过滤条件
GROUP BY  分组列
HAVING    分组后过滤
ORDER BY  排序列 [ASC | DESC]
LIMIT     [偏移,] 行数;
执行顺序(重要):FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。SQL 是声明式语言,写法的顺序 ≠ 执行的顺序。

8.2 基本查询:列选择 / 别名 / DISTINCT

SQLbasic_select.sql
-- 查询所有列(生产环境慎用 *)
SELECT * FROM student;

-- 查询指定列(推荐:按需取列)
SELECT name, age FROM student;

-- AS 别名(可省略 AS)
SELECT name AS 姓名, age AS 年龄
FROM student;

-- DISTINCT 去重
SELECT DISTINCT gender FROM student;
09

WHERE 条件查询 ⭐⭐⭐

WHERE 是 SELECT 的"过滤器"—— 决定返回哪些行。

9.1 比较与逻辑运算符

SQLwhere.sql
-- 比较
WHERE age >= 18

-- AND / OR / NOT
WHERE age >= 18 AND age <= 22
WHERE gender = '男' OR gender = '女'
WHERE NOT (age = 18)

-- BETWEEN:闭区间 [18, 22]
WHERE age BETWEEN 18 AND 22

-- IN:等于列表中任一值
WHERE id IN (1, 2, 3)

-- IS NULL / IS NOT NULL
WHERE email IS NULL
WHERE email IS NOT NULL
陷阱:NULL 不能用 = NULL!= NULL,必须用 IS NULL / IS NOT NULL
10

模糊匹配与条件组合 ⭐⭐⭐

LIKE、IN、BETWEEN —— 让查询更灵活。

10.1 LIKE 模糊匹配

SQLlike.sql
-- 两种通配符:
--   %   匹配任意数量字符(含 0 个)
--   _   匹配恰好 1 个字符

SELECT * FROM student
WHERE name LIKE '张%';           -- 张三、张三丰、张飞(非姓张的不行)

SELECT * FROM student
WHERE name LIKE '张_';           -- 张三、张四(恰好 2 字)

SELECT * FROM student
WHERE email LIKE '%@gmail.com';   -- 以 @gmail.com 结尾

-- 包含"小"的:%小%
-- 第 2 个字是"小"的:_小%

-- 转义特殊字符(MySQL 默认 \)
WHERE name LIKE '50\%' ESCAPE '\';     -- 匹配 "50%"
LIKE 的代价:%abc 这类"前缀为通配符"的查询无法使用索引,会全表扫描。慎用
11

聚合函数与 GROUP BY ⭐⭐⭐

聚合函数把多行"压缩"成一个值;GROUP BY 把数据分组。

11.1 五大聚合函数

函数作用
COUNT(*)行数
COUNT(col)col 非 NULL 的行数
SUM(col)求和
AVG(col)平均
MAX(col) / MIN(col)最大 / 最小
SQLagg.sql
-- 总人数
SELECT COUNT(*) AS total FROM student;

-- 平均年龄
SELECT AVG(age) FROM student;

-- 按性别分组
SELECT gender, COUNT(*) AS cnt, AVG(age) AS avg_age
FROM student
GROUP BY gender;

-- 加上 WHERE 过滤(先过滤再分组)
SELECT class_id, AVG(score) AS avg_score
FROM score
WHERE score >= 60          -- 只看及格的
GROUP BY class_id;

11.2 HAVING:分组后过滤

SQLhaving.sql
-- 各班平均分超过 80 分的班级
SELECT class_id, AVG(score) AS avg_score
FROM score
GROUP BY class_id
HAVING AVG(score) > 80;   -- 分组后的过滤

WHERE vs HAVING:WHERE 是分组前对原始行过滤;HAVING 是分组后对聚合结果过滤。

12

排序与分页 ⭐⭐⭐

ORDER BY 让结果有序,LIMIT 实现分页 —— Web 应用的核心。

12.1 ORDER BY + LIMIT

SQLorder_limit.sql
-- 单字段排序
SELECT * FROM student ORDER BY age ASC;     -- 升序(默认)
SELECT * FROM student ORDER BY age DESC;    -- 降序

-- 多字段排序:先按 age 降序,再按 id 升序
SELECT * FROM student ORDER BY age DESC, id ASC;

-- LIMIT:取前 N 条
SELECT * FROM student LIMIT 10;

-- LIMIT offset, count:分页(offset 从 0 开始)
-- 跳过前 10 条,取接下来的 10 条(第 2 页)
SELECT * FROM student ORDER BY id LIMIT 10, 10;

-- 分页公式:LIMIT (页码 - 1) * 每页, 每页
分页优化:超大表上 LIMIT 1000000, 10 会非常慢。可用WHERE id > last_id 的"游标分页"代替。
13

JOIN 多表查询 ⭐⭐⭐

JOIN 是 SQL 第二个核心 —— 把多张表"拼"起来。

13.1 四种 JOIN 直观图示

A B INNER JOIN = A ∩ B 只返回两表都有匹配的行 A B LEFT JOIN = A 全部 + 交集 左表全保留,右表无匹配则填 NULL A B RIGHT JOIN = B 全部 + 交集 右表全保留
JOIN 类型示意图

13.2 JOIN 实战

SQLjoin_demo.sql
-- INNER JOIN:只返回两表都匹配的行
SELECT s.id, s.name, c.class_name
FROM student s
INNER JOIN class c ON s.class_id = c.id;

-- LEFT JOIN:保留左表全部
SELECT s.id, s.name, c.class_name
FROM student s
LEFT JOIN class c ON s.class_id = c.id;

-- 三表 JOIN:查张三的课程成绩
SELECT s.name, co.course_name, sc.score
FROM student s
JOIN score sc ON s.id = sc.student_id
JOIN course co ON sc.course_id = co.id
WHERE s.name = '张三';
避免笛卡尔积:JOIN 一定要写 ON 条件,否则返回两表行数相乘的"笛卡尔积",数据量爆炸。
14

子查询 ⭐⭐

"查询中的查询" —— 把多个 SELECT 嵌套使用。

14.1 子查询的四种位置

SQLsubquery.sql
-- 1) WHERE 子查询:年龄大于平均年龄
SELECT * FROM student
WHERE age > (SELECT AVG(age) FROM student);

-- 2) IN 子查询:选修了"数据库"的学生
SELECT * FROM student
WHERE id IN (
    SELECT student_id FROM score
    WHERE course_id = (SELECT id FROM course WHERE name = '数据库')
);

-- 3) FROM 子查询:当作临时表
SELECT class_id, avg_score
FROM (SELECT class_id, AVG(score) AS avg_score FROM score GROUP BY class_id) AS t
WHERE avg_score > 80;

-- 4) EXISTS:是否存在
SELECT * FROM course c
WHERE EXISTS (SELECT 1 FROM score WHERE course_id = c.id);
15

数据库设计基础 ⭐⭐⭐

好的设计 = 减少数据冗余 + 避免更新异常。本节讲 ER 模型与三大范式。

15.1 为什么需要数据库设计

❌ 错误设计:所有字段塞一张表

SQLbad_design.sql
CREATE TABLE student (
    name        VARCHAR(50),
    class_name  VARCHAR(50),
    course_1    VARCHAR(50), score_1 INT,
    course_2    VARCHAR(50), score_2 INT,
    course_3    VARCHAR(50), score_3 INT,
    -- 每多一门课就要改表结构
);

✔ 正确设计:拆成 4 张表

SQLgood_design.sql
CREATE TABLE student(id, name, class_id);
CREATE TABLE class(id, class_name);
CREATE TABLE course(id, course_name);
CREATE TABLE score(id, student_id, course_id, score);

15.2 ER 模型(Entity-Relationship) ⭐⭐⭐

学生 课程 成绩 选修 属于 M N 1 : N 学号 课程名 分数
ER 图:实体(矩形)、关系(菱形)、属性(椭圆)

三大范式 ⭐⭐

范式要求目的
1NF字段不可再分(原子性)每个字段只存一个值
2NF非主属性完全依赖主键消除部分依赖
3NF非主属性不传递依赖主键消除"学号 → 班级 → 班主任"的传递

课堂练习 · 第11-15章 SQL 查询

4SELECT 属于 SQL 五大类的哪一类?
5要筛选出"班级人数超过 30 人"的班级,应使用?
6关于 JOIN 说法错误的是?
16

数据完整性约束 ⭐⭐⭐

约束 = 数据库保证数据合法性的内置机制。

16.1 六大约束

约束作用
PRIMARY KEY主键:唯一 + 非空
FOREIGN KEY外键:引用另一张表的主键
UNIQUE唯一约束(可空,但非 NULL 值必须唯一)
NOT NULL非空约束
DEFAULT默认值约束
CHECK检查约束(MySQL 8.0.16+ 才真正生效)
SQLconstraints.sql
CREATE TABLE student (
    id     INT          PRIMARY KEY AUTO_INCREMENT,
    name   VARCHAR(50)  NOT NULL,
    email  VARCHAR(100) UNIQUE,
    age    INT          CHECK (age >= 0 AND age <= 150),
    gender VARCHAR(10)  DEFAULT '未知',
    class_id INT,
    FOREIGN KEY (class_id) REFERENCES class(id)
        ON DELETE CASCADE
);

-- 外键级联:删班级时自动删其学生(慎用)
-- ON DELETE CASCADE / SET NULL / RESTRICT / NO ACTION
17

MySQL 实践 ⭐⭐⭐

从安装到登录到第一个查询 —— 把 MySQL 用起来。

17.1 安装与登录

shellterminal
# macOS 安装
brew install mysql

# 启动服务
brew services start mysql

# Linux
sudo systemctl start mysql

# Windows:从官网下载 MSI 安装包

# 登录
mysql -u root -p

# 查看数据库
mysql> SHOW DATABASES;

# 退出
mysql> exit;

17.2 MySQL 数据类型速查

类别类型
整数TINYINT(1B) · SMALLINT(2B) · INT(4B) · BIGINT(8B)
小数FLOAT · DOUBLE · DECIMAL(p,s)(精确)
字符串CHAR(n) 定长 · VARCHAR(n) 变长 · TEXT · LONGTEXT
日期DATE · TIME · DATETIME · TIMESTAMP(自动时区)· YEAR
其他JSON(MySQL 5.7+)· ENUM · BLOB
DECIMAL vs FLOAT:金额、分数必须用 DECIMAL(精确),不能用 FLOAT/DOUBLE(会精度丢失)。
18

索引 Index ⭐⭐

索引 = 数据库的"目录" —— 让查询从全表扫变为快速定位。

18.1 索引的增删与失效

SQLindex.sql
-- 创建索引(CREATE 时)
CREATE TABLE student (
    id    INT PRIMARY KEY AUTO_INCREMENT,
    name  VARCHAR(50),
    email VARCHAR(100),
    UNIQUE KEY uk_email (email),      -- 唯一索引
    KEY idx_name (name)               -- 普通索引
);

-- 事后添加 / 删除索引
CREATE INDEX idx_age ON student(age);
ALTER TABLE student ADD INDEX idx_class_name(class_name);
DROP INDEX idx_age ON student;

-- 联合索引(多字段索引,列顺序很重要!)
CREATE INDEX idx_name_age ON student(name, age);

-- 查看索引
SHOW INDEX FROM student;

-- EXPLAIN:分析查询是否用上了索引
EXPLAIN SELECT * FROM student WHERE name = '张三';
索引失效的常见原因:LIKE '%abc%'(前缀通配符);WHERE func(col)(函数运算);OR 中部分条件没索引;类型不一致(如 name = 123)。
19

事务 Transaction ⭐⭐⭐

事务 = 一组 SQL 要么全部成功,要么全部失败 —— 数据库正确性的基石。

19.1 ACID ⭐⭐⭐

特性含义
Atomicity 原子性事务内操作要么全成功,要么全失败
Consistency 一致性事务前后数据满足所有约束
Isolation 隔离性并发事务互不干扰
Durability 持久性提交后永久保存,即使断电

19.2 事务实战:银行转账

SQLtransfer.sql
START TRANSACTION;   -- 或 BEGIN

UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 假设这里程序崩溃 / 网络中断……
UPDATE account SET balance = balance + 100 WHERE id = 2;

-- 两步都成功
COMMIT;

-- 任一步失败:
-- ROLLBACK;   -- 撤销所有改动
MySQL 默认模式:InnoDB 引擎 + autocommit=ON → 每条 SQL 自动提交。需要时显式 START TRANSACTION
20

视图 VIEW ⭐⭐

视图 = 一条 SELECT 查询的命名快照,可以像表一样查询。

20.1 视图 CRUD

SQLview.sql
-- 创建视图
CREATE VIEW v_student_score AS
SELECT s.id, s.name, co.course_name, sc.score
FROM student s
JOIN score sc ON s.id = sc.student_id
JOIN course co ON sc.course_id = co.id;

-- 查询视图(和查询表一样)
SELECT * FROM v_student_score WHERE score >= 60;

-- 修改视图
CREATE OR REPLACE VIEW v_student_score AS ...;

-- 删除视图
DROP VIEW v_student_score;

视图的好处:① 简化复杂查询 ② 隐藏表结构(安全)③ 数据独立。

21

窗口函数 Window Function C 拓展

SQL 进阶 —— "分组但保留每行的细节"。MySQL 8.0+ / PostgreSQL / Oracle / SQL Server 全部支持。

21.1 ROW_NUMBER / RANK / DENSE_RANK

SQLwindow.sql
-- 查每个班成绩前 3 名
SELECT * FROM (
    SELECT
        s.class_id,
        s.name,
        sc.score,
        ROW_NUMBER() OVER (PARTITION BY s.class_id ORDER BY sc.score DESC) AS rk
    FROM student s
    JOIN score sc ON s.id = sc.student_id
) t
WHERE rk <= 3;

-- ROW_NUMBER():连续编号(1, 2, 3, 4)
-- RANK():       同分同名,下一空位(1, 2, 2, 4)
-- DENSE_RANK(): 同分同名,下一连续(1, 2, 2, 3)

-- 每行的累计求和
SELECT name, score,
       SUM(score) OVER (ORDER BY id) AS cumulative
FROM score;
窗口函数 vs 聚合函数:聚合函数把多行压缩成一行;窗口函数给每行"附加"一个聚合结果,保留原行
22

Python + MySQL 数据库编程 ⭐⭐

Python 通过 mysql-connectorPyMySQL 连接 MySQL。

22.1 PyMySQL 实战

Pythonpy_mysql.py
import pymysql

# 1) 建立连接
conn = pymysql.connect(
    host="localhost",
    user="root",
    password="123456",
    database="school",
    charset="utf8mb4",
)

# 2) 获取游标
with conn.cursor() as cur:
    # 3) 执行 SQL(参数化防注入!)
    cur.execute(
        "SELECT * FROM student WHERE name = %s",
        ("张三",)
    )
    rows = cur.fetchall()
    for row in rows:
        print(row)

    # 4) 插入 + 提交事务
    cur.execute(
        "INSERT INTO student(name, age) VALUES(%s, %s)",
        ("李四", 20)
    )
    conn.commit()

conn.close()
23

SQL 注入与安全 ⭐⭐⭐

SQL 注入是 Web 安全第一杀手 —— 必须掌握防范方法。

23.1 注入原理

❌ 危险代码:字符串拼接

Pythonbad.py
username = input("用户名:")
sql = f"SELECT * FROM user WHERE name = '{username}'"
cur.execute(sql)

# 如果用户输入:
#   ' OR '1'='1
# SQL 变成:
# SELECT * FROM user WHERE name = '' OR '1'='1'
# → 返回所有用户!

✔ 安全代码:参数化查询

Pythongood.py
username = input("用户名:")
sql = "SELECT * FROM user WHERE name = %s"
cur.execute(sql, (username,))
# 数据库驱动会自动转义恶意字符
铁律:任何来自用户输入的数据都不能直接拼到 SQL 里 —— 必须用参数化查询 / 预编译语句。
24

综合项目:数据库设计实战

3 个项目覆盖学生管理、图书管理、电商三大场景 —— 体现"前端 → 后端 → SQL → MySQL"完整链路。

项目一:学生成绩管理系统 ⭐⭐⭐

难度 ★★★ · SQL DDL + DML + JOIN + 聚合
SQLschool.sql
CREATE DATABASE school DEFAULT CHARACTER SET utf8mb4;
USE school;

CREATE TABLE class (
    id   INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL
);

CREATE TABLE student (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    name     VARCHAR(50) NOT NULL,
    class_id INT,
    FOREIGN KEY (class_id) REFERENCES class(id)
);

CREATE TABLE course (
    id   INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL
);

CREATE TABLE score (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT,
    course_id  INT,
    score      DECIMAL(5,2),
    FOREIGN KEY (student_id) REFERENCES student(id),
    FOREIGN KEY (course_id)  REFERENCES course(id)
);

-- 常用查询
-- 1. 查每个学生的总分、平均分
SELECT s.name, SUM(sc.score) AS total, AVG(sc.score) AS avg
FROM student s JOIN score sc ON s.id = sc.student_id
GROUP BY s.id, s.name;

-- 2. 查每门课的最高分、最低分、参考人数
SELECT c.name, MAX(sc.score), MIN(sc.score), COUNT(*)
FROM course c JOIN score sc ON c.id = sc.course_id
GROUP BY c.id;

-- 3. 各班平均分排名(窗口函数)
SELECT c.name, AVG(sc.score) AS avg_score,
       RANK() OVER (ORDER BY AVG(sc.score) DESC) AS rk
FROM class c
JOIN student s ON c.id = s.class_id
JOIN score sc ON s.id = sc.student_id
GROUP BY c.id, c.name;

项目二:图书管理系统 ⭐⭐⭐

难度 ★★★ · SQL + 视图 + 存储过程

含图书 / 读者 / 借阅记录 / 分类四张表。实现借书、还书、逾期查询。

项目三:电商数据库 ⭐⭐⭐

难度 ★★★★ · 一对多 / 多对多 / 事务 / 索引

用户、商品、订单、订单项、支付表。涉及一对多 + 多对多关系,需要用中间表(如收藏夹)实现。重点学习:JOIN + 事务 + 索引优化。

综合项目学习建议
  • 从 SQL 设计开始:先在纸上画 ER 图,再写 CREATE TABLE;
  • 用真实数据填充每张表(5~10 条即可);
  • 每个功能写出对应的 SELECT 语句;
  • 最后接入 Python / Java:体会"前端 → 后端 → SQL"的完整链路;
  • 加分项:用 EXPLAIN 分析你的 SQL 是否用上了索引。

SQL 速查手册

写 SQL 时随手翻一翻 —— 比每次去搜更快。

DDL 数据定义速查

SQLDDL cheat
CREATE DATABASE db_name;

CREATE TABLE t (
    id    INT PRIMARY KEY AUTO_INCREMENT,
    name  VARCHAR(50) NOT NULL DEFAULT '',
    age   INT CHECK (age >= 0),
    email VARCHAR(100) UNIQUE,
    FOREIGN KEY (dept_id) REFERENCES dept(id)
);

ALTER TABLE t
    ADD COLUMN phone VARCHAR(20),
    DROP COLUMN phone,
    MODIFY name VARCHAR(100),
    RENAME TO new_t;

DROP TABLE t;
TRUNCATE TABLE t;

DML + DQL 速查

SQLDML/DQL
INSERT INTO t (a, b) VALUES (1, 'x');

UPDATE t SET a = 2 WHERE id = 1;

DELETE FROM t WHERE a > 0;

SELECT col1, col2, COUNT(*), SUM(col3)
FROM t
WHERE col1 LIKE 'a%' AND col2 BETWEEN 1 AND 10
GROUP BY col1
HAVING COUNT(*) > 3
ORDER BY col2 DESC
LIMIT 10;

JOIN 类型速查

类型结果
INNER JOIN两表都有匹配的行
LEFT JOIN左表全部 + 右表匹配(无匹配则 NULL)
RIGHT JOIN右表全部 + 左表匹配
CROSS JOIN笛卡尔积(慎用)
SELF JOIN自己连自己(解决"行内比较")

常见错误与防范

  • UPDATE / DELETE 忘带 WHERE → 整表更新/删除
  • SELECT * 在生产环境 → 浪费 IO、不稳定
  • ❌ 字符串拼接用户输入 → SQL 注入
  • ❌ 主键没有索引 → 全表扫描
  • ❌ 大表 LIMIT 1000000, 10 → 性能灾难
  • WHERE 过滤 聚合函数 → 应改为 HAVING
  • NULL = NULL 比较 → 永远为 NULL,应改为 IS NULL
@media print{ #topnav,#progress,.back-top{display:none!important} .hero{padding:20px 0!important;background:none!important;color:#000!important} .hero h1{color:#000!important} .hero .sub,.hero .hero-chips{color:#333!important} section{break-inside:avoid;box-shadow:none!important;border:1px solid #ccc!important} pre{background:#f5f5f5!important;color:#000!important} }