CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
SQL 与关系型数据库/课时/关系型数据库与 SQL 是什么
RawBlame
课时目录10 节

SQL 与关系型数据库

  1. 01关系型数据库与 SQL 是什么
  2. 02SELECT 基本查询
  3. 03WHERE 过滤与排序
  4. 04JOIN 多表联结
  5. 05聚合与 GROUP BY
  6. 06INSERT、UPDATE、DELETE
  7. 07表设计与主键外键
  8. 08索引与查询性能
  9. 09事务基础
  10. 10综合练习与本站数据模型

关系型数据库与 SQL 是什么

表、行、列;MySQL 在本站架构中的位置。

25 分钟

关系型数据库与 SQL 是什么

这一课你将能做到

  1. 用自己的话说清楚表、行、列、主键、外键,并说出本站 cf_track / cf_lesson 各对应什么
  2. 连上 MySQL 8,跑通一套贯穿全课程的建库 + 造数脚本,SHOW TABLES 能看到 5 张练习表
  3. 区分 DQL / DML / DDL 三类 SQL,并执行人生第一条 SELECT

为什么要学

CodeRoadMap 是标准的 Java 后端架构:Spring Boot 提供接口,MySQL 存数据。你在页面上看到的「课程列表」「课时侧栏」「学完打勾」,最终都是后端执行一句 SQL,把表里的行查出来,转成 JSON 返回给前端。

这意味着两件事:

  • 排查「页面没数据」时,最快的验证方式不是翻 Java 日志,而是拿同样的条件去库里查一遍——数据在不在,一查便知。
  • 读写接口的性能问题,十有八九落在 SQL 上。表设计烂、SQL 写烂,Java 层写得再漂亮也救不回来。

人话:关系型数据库就是一叠规则极严的表格;SQL 是你跟这些表格说话的语言。你说「把 category 是 database 的课程给我」,它就照办——你只描述要什么,不用写循环。

先建立图像

浏览器
   ↓  HTTP 请求 /api/tracks
Spring Boot(业务逻辑、权限校验)
   ↓  JDBC / MyBatis:把 SQL 发给数据库
MySQL(数据的真相:表里的行)
   cf_user、cf_track、cf_lesson、cf_progress …
词人话本站对应
表 table一类东西的集合cf_track = 课程
行 row一条记录一门「SQL 与关系型数据库」
列 column一个字段title、category、level
主键 PK唯一认出这一行的身份证自增 id
外键 FK指向别的表主键的引用cf_lesson.track_id → cf_track.id

Excel 也能画表格,为什么还要数据库?Excel 单元格想填什么填什么,找重复靠肉眼;数据库每列类型固定、行与行靠键关联、支持多用户并发写入、断电不丢数据、能建索引加速查询。网站要的恰恰是这些。

跟着做

本课程 10 课共用一套练习数据:一个迷你在线课程平台(学生、课程、课时、学习记录、订单)。本课建好后,一直用到最后一课。

步骤 1:连上 MySQL 8

本机装了 MySQL 8 的话,打开终端:

mysql -u root -p

输入密码进入 mysql> 提示符,先确认版本:

SELECT VERSION();

看到 8.x.x 就对了(本站后端用的就是 MySQL 8)。不想装本机,用 Docker 起一个也行:

docker run -d --name mysql8 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:8.0
docker exec -it mysql8 mysql -uroot -p123456

步骤 2:建练习库

DROP DATABASE IF EXISTS sql_practice;
CREATE DATABASE sql_practice DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE sql_practice;

DROP 在这里是为了脚本可反复重跑:任何时候把数据玩坏了,回到第一课整段重跑,就回到初始状态。

步骤 3:建表 + 造数(全课程通用脚本)

下面脚本一次建 5 张表、插好数据,后面 9 课全用它。整段复制执行即可:

-- 学生表
CREATE TABLE student (
    id         BIGINT PRIMARY KEY AUTO_INCREMENT,
    name       VARCHAR(32)   NOT NULL,
    email      VARCHAR(128)  NOT NULL UNIQUE,
    city       VARCHAR(32)   NOT NULL,
    vip        TINYINT(1)    NOT NULL DEFAULT 0,
    balance    DECIMAL(10,2) NOT NULL DEFAULT 0.00,
    created_at DATETIME      DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 课程表
CREATE TABLE course (
    id         BIGINT PRIMARY KEY AUTO_INCREMENT,
    slug       VARCHAR(64)   NOT NULL UNIQUE,
    title      VARCHAR(128)  NOT NULL,
    category   VARCHAR(32)   NOT NULL,                 -- java / frontend / database
    level      VARCHAR(16)   NOT NULL DEFAULT 'beginner',
    price      DECIMAL(10,2) NOT NULL DEFAULT 0.00,
    status     VARCHAR(16)   NOT NULL DEFAULT 'draft', -- draft / published
    created_at DATETIME      DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 课时表(一门课有多节课)
CREATE TABLE lesson (
    id           BIGINT PRIMARY KEY AUTO_INCREMENT,
    course_id    BIGINT NOT NULL,
    title        VARCHAR(128) NOT NULL,
    order_index  INT    NOT NULL,
    duration_min INT    NOT NULL DEFAULT 20,
    status       VARCHAR(16) NOT NULL DEFAULT 'draft',
    CONSTRAINT fk_lesson_course FOREIGN KEY (course_id)
        REFERENCES course(id) ON DELETE CASCADE
) ENGINE=InnoDB;

-- 学习记录表(谁在学哪节课、完成没有)
CREATE TABLE study_record (
    id           BIGINT PRIMARY KEY AUTO_INCREMENT,
    student_id   BIGINT NOT NULL,
    lesson_id    BIGINT NOT NULL,
    completed    TINYINT(1) NOT NULL DEFAULT 0,
    score        INT DEFAULT NULL,
    completed_at DATETIME DEFAULT NULL,
    UNIQUE KEY uk_student_lesson (student_id, lesson_id),
    CONSTRAINT fk_record_student FOREIGN KEY (student_id)
        REFERENCES student(id) ON DELETE CASCADE,
    CONSTRAINT fk_record_lesson FOREIGN KEY (lesson_id)
        REFERENCES lesson(id) ON DELETE CASCADE
) ENGINE=InnoDB;

-- 订单表(ORDER 是 SQL 保留字,所以表名叫 pay_order)
CREATE TABLE pay_order (
    id         BIGINT PRIMARY KEY AUTO_INCREMENT,
    student_id BIGINT        NOT NULL,
    course_id  BIGINT        NOT NULL,
    amount     DECIMAL(10,2) NOT NULL,
    status     VARCHAR(16)   NOT NULL DEFAULT 'PAID',  -- PAID / REFUNDED
    created_at DATETIME      DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_order_student FOREIGN KEY (student_id) REFERENCES student(id),
    CONSTRAINT fk_order_course  FOREIGN KEY (course_id)  REFERENCES course(id)
) ENGINE=InnoDB;

-- 造数:7 个学生
INSERT INTO student (id, name, email, city, vip, balance, created_at) VALUES
(1,'老陈','chen@example.com','深圳',1,128.00,'2026-07-01 09:00:00'),
(2,'小林','lin@example.com','杭州',0, 39.00,'2026-07-03 10:00:00'),
(3,'阿珍','zhen@example.com','广州',1,256.50,'2026-07-05 11:00:00'),
(4,'大鹏','peng@example.com','北京',0,  0.00,'2026-07-08 14:00:00'),
(5,'小夏','xia@example.com','深圳',0, 88.00,'2026-07-10 16:00:00'),
(6,'老周','zhou@example.com','上海',1,500.00,'2026-07-12 18:00:00'),
(7,'小吴','wu@example.com','成都',0, 20.00,'2026-07-15 20:00:00');

-- 造数:7 门课程
INSERT INTO course (id, slug, title, category, level, price, status, created_at) VALUES
(1,'java-fundamentals','Java 基础入门','java','beginner',199.00,'published','2026-06-01 10:00:00'),
(2,'java-oop','Java 面向对象进阶','java','intermediate',299.00,'published','2026-06-03 10:00:00'),
(3,'sql-essentials','SQL 与关系型数据库','database','beginner',0.00,'published','2026-06-05 10:00:00'),
(4,'mysql-advanced','MySQL 性能优化实战','database','advanced',399.00,'published','2026-06-08 10:00:00'),
(5,'html-css','前端页面基础','frontend','beginner',99.00,'published','2026-06-10 10:00:00'),
(6,'redis-intro','Redis 缓存入门','database','intermediate',0.00,'draft','2026-06-12 10:00:00'),
(7,'git-essentials','Git 团队协作基础','frontend','beginner',49.00,'draft','2026-06-15 10:00:00');

-- 造数:14 节课时
INSERT INTO lesson (id, course_id, title, order_index, duration_min, status) VALUES
(1,1,'环境搭建与第一个程序',1,25,'published'),
(2,1,'变量与数据类型',2,30,'published'),
(3,1,'流程控制',3,35,'draft'),
(4,2,'类与对象',1,40,'published'),
(5,2,'继承与多态',2,45,'published'),
(6,3,'关系型数据库与 SQL 是什么',1,25,'published'),
(7,3,'SELECT 基本查询',2,30,'published'),
(8,3,'WHERE 过滤与排序',3,30,'published'),
(9,3,'JOIN 多表联结',4,35,'draft'),
(10,4,'索引原理与 B+ 树',1,50,'published'),
(11,4,'执行计划 EXPLAIN 详解',2,55,'published'),
(12,5,'HTML 文档结构',1,20,'published'),
(13,5,'CSS 盒模型',2,28,'published'),
(14,6,'Redis 是什么',1,15,'draft');

-- 造数:17 条学习记录
INSERT INTO study_record (id, student_id, lesson_id, completed, score, completed_at) VALUES
(1,1,6,1,92,'2026-08-10 10:00:00'),
(2,1,7,1,88,'2026-08-11 09:30:00'),
(3,1,1,1,95,'2026-08-05 14:00:00'),
(4,1,2,1,90,'2026-08-06 15:20:00'),
(5,2,6,1,75,'2026-08-15 20:10:00'),
(6,2,12,1,80,'2026-08-16 21:00:00'),
(7,3,6,1,98,'2026-08-01 08:40:00'),
(8,3,7,1,95,'2026-08-02 09:00:00'),
(9,3,8,1,91,'2026-08-03 10:00:00'),
(10,3,10,1,85,'2026-08-09 11:00:00'),
(11,4,1,1,60,'2026-08-20 19:00:00'),
(12,4,6,0,NULL,NULL),
(13,5,7,1,83,'2026-08-25 16:30:00'),
(14,6,6,1,90,'2026-08-12 12:00:00'),
(15,6,10,1,88,'2026-08-13 13:00:00'),
(16,6,11,1,92,'2026-08-14 14:00:00'),
(17,6,4,1,85,'2026-08-18 15:00:00');

-- 造数:6 笔订单
INSERT INTO pay_order (id, student_id, course_id, amount, status, created_at) VALUES
(1,3,1,199.00,'PAID','2026-08-01 09:00:00'),
(2,1,4,399.00,'PAID','2026-08-02 10:00:00'),
(3,3,4,399.00,'PAID','2026-08-03 11:00:00'),
(4,6,5, 99.00,'PAID','2026-08-05 12:00:00'),
(5,2,1,199.00,'REFUNDED','2026-08-06 13:00:00'),
(6,5,5, 99.00,'PAID','2026-08-08 14:00:00');

步骤 4:检查建出来的东西

SHOW TABLES;                    -- 应看到 5 张表
DESC student;                   -- 看学生表的列定义
SELECT COUNT(*) FROM student;   -- 7
SELECT COUNT(*) FROM lesson;    -- 14

DESC(describe)会列出每列的名字、类型、是否允许 NULL、默认值。以后拿到任何陌生表,第一件事就是 DESC 它;更完整的信息用 SHOW CREATE TABLE student; 看。

步骤 5:执行第一条 SELECT

SELECT slug, title, category, price
FROM course
WHERE status = 'published';

应返回 5 行(7 门课里有 2 门是 draft)。这就是 SQL 的基本形状:从哪张表取(FROM)、要哪些列(SELECT)、要满足什么条件(WHERE)。

步骤 6:认识 SQL 三大类

类型关键字干什么本课程节奏
DQL 查询SELECT只读地问数据第 2–5 课,重头戏
DML 改数INSERT UPDATE DELETE增删改数据第 6 课
DDL 改结构CREATE ALTER DROP建表改表删表本课脚本 + 第 7 课

日常后端开发 80% 的时间在写 DQL,DML 次之,DDL 通常交给迁移脚本(如 Flyway)统一管理。

讲清楚要点

  1. 「关系型」的核心是表 + 键:同一份事实只存一次(课程标题只存在 course 表),别处用外键引用,需要时 JOIN 拼回来。这是它和「到处复制粘贴的 Excel」的本质区别。
  2. SQL 是声明式语言:你描述结果,不描述过程。没有 for 循环、没有中间变量,怎么执行由数据库优化器决定——这也是为什么同一条 SQL 换个索引速度天差地别。
  3. MySQL 8 是本站方言:字符集统一 utf8mb4(能存 emoji),存储引擎用 InnoDB(支持事务和外键,第 9 课会用到)。PostgreSQL、Oracle 语法有差异,别混着背。
  4. 主键和外键是骨架:主键保证「一行能被唯一找到」,外键保证「引用的关系必须真实存在」。第 7 课表设计会展开。
  5. 框架不取代 SQL:MyBatis / JPA 只是帮你把 Java 对象和 SQL 互转,SQL 本身还是你写的。
  6. 环境隔离是纪律:练习在 sql_practice,本站真实库叫 codeforge。对真实库,只读 SELECT 可以放心跑,任何写操作先想三遍。

你可能会懵的地方

  • 「装了 MySQL」和「建了库」是什么关系? 安装的是 MySQL 服务(一个进程),里面可以同时有很多个 DATABASE。sql_practice 和 codeforge 是同一个服务里的两个库,USE xxx 决定当前语句落在哪个库。

  • 不装 MySQL,用在线工具行吗? 跑前几课可以(db-fiddle、SQL OnLine 都支持贴脚本)。但索引课要看执行计划、事务课要开两个会话对比,本地或 Docker 环境更稳。

  • SQL 关键字要大写吗? 不区分大小写,select 和 SELECT 等价。惯例是关键字大写、表名列名小写,纯为了人眼好读。本课程遵循这个惯例。

  • 为什么表名不叫 order 而叫 pay_order? ORDER 是 SQL 保留字(排序子句 ORDER BY 里的那个)。用保留字当表名列名,每次都要加反引号 `order`,自找麻烦。避开保留字是基本素养。

  • AUTO_INCREMENT 是什么? 自增:插入时不指定 id,数据库自动分配 1、2、3……这是 MySQL 里主键最常用的生成方式,保证唯一且基本有序(第 7、8 课会讲为什么这很重要)。

错法对照

错法问题正解
连上 codeforge 就开始练 DROP真实数据没了只在 sql_practice 练手
背关键字不连库三天全忘每课脚本亲手跑一遍
把 SQL 当 Excel 公式填语法混乱、语义不明按 SELECT … FROM … WHERE 骨架写
表名列名用保留字(order、desc)到处加反引号还容易报错换名:pay_order、sort_order
混用 PostgreSQL 语法MySQL 直接报错本课程统一 MySQL 8 写法
建库不指定 utf8mb4中文/emoji 乱码建库语句带 DEFAULT CHARSET utf8mb4

卡点急救

  1. mysql 命令找不到:MySQL 的 bin 目录没进 PATH。用安装目录下 mysql.exe 的全路径,或直接用 Workbench / DBeaver / DataGrip 图形客户端。
  2. Access denied for user 'root'@'localhost':密码错。Docker 方式按上面给的 -p123456;本机忘了密码就重置或新建一个学习用账号。
  3. 脚本中途报错:看第一条报错所在行(多半是表已存在)。先执行开头三条 DROP/CREATE/USE,再从 CREATE TABLE 起重跑。
  4. 中文变问号:连接没走 utf8mb4。命令行加 --default-character-set=utf8mb4;JDBC 连接串加 characterEncoding=utf8。

面试常问

  • 关系型数据库和非关系型数据库的区别? 关系型:表结构固定、强约束、支持事务和复杂 JOIN,适合订单、账务这类强一致数据。非关系型(Redis、MongoDB):结构灵活、水平扩展容易,适合缓存、日志、文档。本站两者都用了:业务数据在 MySQL,热点缓存用 Redis。

  • 说说 DDL、DML、DQL 的区别? DDL 改结构(CREATE/ALTER/DROP),DML 改数据(INSERT/UPDATE/DELETE),DQL 查数据(SELECT)。注意 DDL 通常隐式提交、无法回滚,生产上变更要格外谨慎。

  • 为什么选 InnoDB 而不是 MyISAM? InnoDB 支持事务、行级锁、外键和崩溃恢复,MyISAM 都不支持。MySQL 5.5 起 InnoDB 是默认引擎,现代项目没有理由选 MyISAM。

  • utf8 和 utf8mb4 的区别? MySQL 的 utf8 实际最多 3 字节,存不了 emoji 和部分生僻字;utf8mb4 才是完整的 4 字节 UTF-8。新项目一律 utf8mb4。

自测三问

  1. cf_lesson.track_id 这一列为什么存在?没有它,页面会发生什么?
  2. CREATE TABLE 属于哪一类 SQL?UPDATE 呢?
  3. 练习库里现在有哪 5 张表?pay_order 为什么不叫 order?

小结一句话

关系型数据库用「表 + 主键 + 外键」组织真相,SQL 用一句声明式的话就取到结果;全课程 10 课都建立在本课这套 sql_practice 练习数据上。

延伸

  • MySQL 8.0 参考手册(当字典查,别整本啃)
  • 下一课:SELECT 基本查询——学会「要哪几列、叫什么名、去掉重复」。
加载本节练习…
下一课 →SELECT 基本查询