MySQL基础入门:从零理解数据库与SQL

后端开发绕不开数据库,而 MySQL 是绝大多数人接触的第一款关系型数据库。从一个小型博客到千万用户的电商平台,MySQL 都在背后默默存着每一行数据。本文带你从"数据库是什么"开始,到亲手建库建表、插入查询数据,搭建一个完整的"学生选课系统"数据模型。不需要任何前置知识。

为什么需要数据库

写一个简单的脚本时,把数据存到文本文件或 Excel 里确实够用。来对比一下这两种方式的差异:

场景 纯文件/Excel 数据库
查询"所有及格的学生的姓名" 写代码遍历整个文件 一条 SELECT 语句
多个用户同时写入 文件锁、数据覆盖、竞态条件 数据库自动处理并发
防止存入不合法的数据 全靠代码校验,换个入口就绕过 数据库约束兜底
数据量到百万级 一个 Excel 文件打不开 照常运行
误删后找回 取决于有没有备份文件 事务回滚、日志恢复

数据库本质上是一个专门管理数据的独立程序。你把数据交给它,它帮你安全地存、高效地查、并发地写,同时保证数据不会乱。

关系型数据库与 MySQL

什么是关系型数据库

"关系型"这个词听起来抽象,其实就对应一个你非常熟悉的东西------表格

复制代码
┌──────────────────────────────────────────────────────┐
│                    学生表 (students)                   │
├────┬──────────┬─────┬──────────────────┬──────────────┤
│ id │   name   │ age │      email       │  enrolled_at │
├────┼──────────┼─────┼──────────────────┼──────────────┤
│  1 │ 张三     │  20 │ zhangsan@qq.com  │ 2024-09-01   │
│  2 │ 李四     │  21 │ lisi@qq.com      │ 2024-09-01   │
│  3 │ 王五     │  19 │ wangwu@qq.com    │ 2024-09-01   │
└────┴──────────┴─────┴──────────────────┴──────────────┘

每一行是一个学生,每一列是学生的某个属性。关系型数据库就是由这样一张张表格组成的系统,表与表之间通过"关系"连接------比如一张"选课表"同时关联学生和课程。

这引出了一个核心概念------SQL (Structured Query Language):一种专门用来和关系型数据库对话的语言。你用 SQL 告诉数据库"帮我查一下张三选了哪些课",它返回结果。所有主流关系型数据库(MySQL、PostgreSQL、Oracle、SQL Server)都使用 SQL,学会 SQL 就意味着你能操作其中任何一种。

MySQL 的位置

MySQL 是关系型数据库中的"瑞士军刀"------轻量、免费、文档多、社区大。Facebook 早期用它,国内互联网公司的标配之一。现在由 Oracle 维护,同时有开源的社区版和完全兼容的替代品 MariaDB。本文以 MySQL 8.0 社区版为准。

关系型数据库还有很多选择:PostgreSQL 在功能完整性和标准兼容性上更强,SQLite 是一个零配置的文件数据库(Django 默认用它做开发)。MySQL 的优势在于部署简单、读性能好、运维工具成熟,作为入门选择非常合适。

环境准备:安装 MySQL

用 Docker 是最简单的方式------一条命令就能跑起来,不会弄乱系统环境。

bash 复制代码
docker run -d \
  --name mysql-tutorial \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=school \
  mysql:8.0

参数说明:---name 给容器命名,-p 3306:3306 把容器的 MySQL 端口映射到本机,-e MYSQL_ROOT_PASSWORD 设置 root 密码,-e MYSQL_DATABASE 启动时自动创建一个叫 school 的数据库。执行完等几秒,MySQL 就准备好了。

连进去看看:

bash 复制代码
docker exec -it mysql-tutorial mysql -uroot -p123456

出现 mysql> 提示符就说明连接成功。后续所有 SQL 语句都在这个交互式终端里执行。

如果不想用 Docker,也可以去 dev.mysql.com/downloads 下载安装包。安装过程中会要求设置 root 密码,记住它,后面登录用的就是 mysql -uroot -p

核心概念拆解

在写 SQL 之前,先理清几个基本概念。它们之间的关系可以概括为:

复制代码
数据库 (database)
  └── 表 (table)
        ├── 列 (column) / 字段 (field) ------ 定义数据的结构
        └── 行 (row) / 记录 (record) ------ 实际存的数据
              └── 主键 (primary key) ------ 每行的唯一标识
              └── 外键 (foreign key) ------ 指向另一张表的数据行

数据库 (Database)

数据库是表的容器,一个数据库里可以有多张表。 你可以把数据库理解为一个项目的命名空间------"学生管理系统"用一个库,"电商系统"用另一个库,互不干扰。

sql 复制代码
-- 查看有哪些数据库
SHOW DATABASES;

你会看到几个系统自带的库(mysqlinformation_schema 等),以及我们创建容器时指定的 school

表 (Table)

表是数据存储的实际载体。在建表之前,你得先决定有哪些列、每列是什么类型。这一步叫定义表结构(schema),是整个数据库设计的起点。

主键 (Primary Key)

主键是表中每行数据的唯一身份证号。 它有两个铁律:

  1. 不能为空:每行必须有一个主键值
  2. 不能重复:两张不同的行不能有相同的主键

最常见的做法是设一个自增整数 id 作为主键,每次插入新行时自动 +1。

外键 (Foreign Key)

外键是表与表之间建立关系的机制。 比如"选课记录表"里有一个 student_id 列,它的值必须来自"学生表"的某个主键 id------这个 student_id 就是外键。

外键的好处是数据库会帮你把关:比如你不能插入一条选课记录,引用一个不存在的学生 ID。这种"引用完整性"是关系型数据库的核心价值之一。

认识数据类型

建表之前必须知道有哪些数据类型可用。MySQL 的数据类型可以分成三大类:

数值类型

类型 范围 典型用途
TINYINT -128 ~ 127 年龄、状态码
INT -21亿 ~ 21亿 数量、ID
BIGINT 极大整数 大表主键、时间戳
DECIMAL(M, D) 精确小数 金额(关键:金额永远不用 FLOAT
FLOAT / DOUBLE 近似浮点 科学计算、评分

金额为什么不能用 FLOAT?因为浮点数存储的是近似值,0.1 + 0.2 在浮点世界里不等于 0.3。金额计算差一分钱都是事故,所以用精确的 DECIMAL

字符串类型

类型 特点 典型用途
CHAR(N) 定长,最多 255 字符 性别、状态码等固定长度的字段
VARCHAR(N) 变长,最多 65535 字符 姓名、邮箱、标题等变长文本
TEXT 长文本 文章正文、简介

VARCHAR 是最常用的字符串类型VARCHAR(50) 表示最多存 50 个字符,但实际只占用你存进去的长度 + 1 字节,不会浪费空间。

日期时间类型

类型 格式 说明
DATE YYYY-MM-DD 只存日期
TIME HH:MM:SS 只存时间
DATETIME YYYY-MM-DD HH:MM:SS 日期+时间,范围 1000~9999 年
TIMESTAMP 同上 日期+时间,范围 1970~2038 年,自动跟随时区

日常开发里 DATETIME 用得最多------范围大、行为直观。TIMESTAMP 在需要"记录何时创建、何时更新"且希望数据库自动维护的场景下更合适。

第一条 SQL:建库建表

创建并使用数据库

sql 复制代码
-- 创建一个数据库
CREATE DATABASE school;

-- 切换到该数据库(后续操作都针对它)
USE school;

因为我们用 Docker 启动时指定了 MYSQL_DATABASE=school,库已经存在,直接用 USE school; 即可。

创建学生表

sql 复制代码
CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    age TINYINT,
    email VARCHAR(100) UNIQUE,
    enrolled_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

逐行解读:

  • id INT AUTO_INCREMENT PRIMARY KEY:整数主键,每次插入时自动 +1
  • name VARCHAR(50) NOT NULL:名字最多 50 个字符,不能为空
  • age TINYINT:年龄,没有 NOT NULL,允许不填
  • email VARCHAR(100) UNIQUE:邮箱,不能重复
  • enrolled_at DATETIME DEFAULT CURRENT_TIMESTAMP:入学时间,不指定时自动填当前时间

创建课程表

sql 复制代码
CREATE TABLE courses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    credit TINYINT NOT NULL DEFAULT 1,
    teacher VARCHAR(50) NOT NULL
);

每门课有名称、学分和授课老师。DEFAULT 1 表示不填学分时默认为 1 分。

创建选课表

这是"学生"和"课程"之间的桥梁------一个学生可以选多门课,一门课可以有多个学生,这是典型的多对多关系

sql 复制代码
CREATE TABLE enrollments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    score DECIMAL(4, 1),
    enrolled_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (student_id) REFERENCES students(id),
    FOREIGN KEY (course_id) REFERENCES courses(id)
);

关键点:

  • score DECIMAL(4, 1):分数,总共 4 位数字,其中 1 位是小数(如 85.5100.0
  • FOREIGN KEY (student_id) REFERENCES students(id)student_id 必须是 students 表中存在的 id;如果试图插入一个不存在的学生 ID,数据库会直接拒绝
  • FOREIGN KEY (course_id) REFERENCES courses(id):同理,课程 ID 必须存在

执行 SHOW TABLES; 确认三张表都已创建:

sql 复制代码
SHOW TABLES;
-- +------------------+
-- | Tables_in_school |
-- +------------------+
-- | courses          |
-- | enrollments      |
-- | students         |
-- +------------------+

再用 DESCRIBESHOW COLUMNS FROM 可以查看任意表的结构:

sql 复制代码
DESCRIBE students;

数据操作:增删改查

现在三张空表就绪,正式开始往里面填数据。

INSERT:插入数据

sql 复制代码
-- 插入学生
INSERT INTO students (name, age, email) VALUES
    ('张三', 20, 'zhangsan@example.com'),
    ('李四', 21, 'lisi@example.com'),
    ('王五', 19, 'wangwu@example.com');

-- 插入课程
INSERT INTO courses (name, credit, teacher) VALUES
    ('高等数学', 4, '陈教授'),
    ('大学英语', 3, '刘老师'),
    ('数据库原理', 3, '张教授'),
    ('数据结构', 4, '王教授');

注意 INSERT 的语法:INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...)id 字段是自增的,不用手动指定;enrolled_at 有默认值,也不用写。

再插入选课记录,建立学生和课程之间的关联:

sql 复制代码
-- 张三选了高等数学和数据库原理
INSERT INTO enrollments (student_id, course_id, score) VALUES
    (1, 1, 92.5),   -- 张三 → 高等数学
    (1, 3, 88.0);   -- 张三 → 数据库原理

-- 李四选了三门课
INSERT INTO enrollments (student_id, course_id, score) VALUES
    (2, 1, 76.0),   -- 李四 → 高等数学
    (2, 2, 85.5),   -- 李四 → 大学英语
    (2, 4, NULL);   -- 李四 → 数据结构(还没出成绩)

-- 王五选了两门
INSERT INTO enrollments (student_id, course_id, score) VALUES
    (3, 3, 91.0),   -- 王五 → 数据库原理
    (3, 4, 79.0);   -- 王五 → 数据结构

当你要插入很多行时,一条 INSERT 塞多个 VALUES 比分开写多条 INSERT 高效得多。数据库事务的开销集中在语句边界上,批量插入能显著减少这个开销。

SELECT:查询数据

SELECT 是 SQL 里最复杂也最重要的语句。这里先讲基础用法,复杂查询留到下一篇文章展开。

查全表:

sql 复制代码
SELECT * FROM students;

* 表示所有列。输出:

复制代码
+----+--------+------+------------------------+---------------------+
| id | name   | age  | email                  | enrolled_at         |
+----+--------+------+------------------------+---------------------+
|  1 | 张三   |   20 | zhangsan@example.com   | 2026-07-09 18:00:00 |
|  2 | 李四   |   21 | lisi@example.com       | 2026-07-09 18:00:00 |
|  3 | 王五   |   19 | wangwu@example.com     | 2026-07-09 18:00:00 |
+----+--------+------+------------------------+---------------------+

只查特定列:

sql 复制代码
SELECT name, email FROM students;

加条件筛选(WHERE):

sql 复制代码
-- 查年龄大于等于 20 的学生
SELECT name, age FROM students WHERE age >= 20;

-- 查选了课程 1(高等数学)的选课记录
SELECT * FROM enrollments WHERE course_id = 1;

排序(ORDER BY):

sql 复制代码
-- 按年龄降序排列
SELECT name, age FROM students ORDER BY age DESC;

-- 按入学时间升序(默认就是 ASC,可以不写)
SELECT name, enrolled_at FROM students ORDER BY enrolled_at ASC;

限制返回行数(LIMIT):

sql 复制代码
-- 只查年龄最大的两个学生
SELECT name, age FROM students ORDER BY age DESC LIMIT 2;

UPDATE:修改数据

sql 复制代码
-- 王五过生日,年龄 +1
UPDATE students SET age = 20 WHERE name = '王五';

WHERE 子句是 UPDATE 的保险丝。 如果你写了 UPDATE students SET age = 20; 却忘了 WHERE,数据库会忠实地把所有人的年龄都改成 20。

当你执行 UPDATE 或 DELETE 时,先写 WHERE 条件再补前面的部分,或者先在 SELECT 里跑一遍相同的条件确认命中范围------这是很多 DBA 用血泪换来的肌肉记忆。

DELETE:删除数据

sql 复制代码
-- 退选:删除某条选课记录
DELETE FROM enrollments WHERE student_id = 3 AND course_id = 4;

同理,DELETE 必须带 WHEREDELETE FROM students; 会清空整张表。万一真的误删了,还有最后一根救命稻草:

sql 复制代码
-- 开启事务后再操作,确认无误再提交
START TRANSACTION;
DELETE FROM enrollments WHERE student_id = 1;
-- 检查一下删对了没
SELECT * FROM enrollments WHERE student_id = 1;
-- 确认无误再提交,搞错了就 ROLLBACK
COMMIT;

事务的概念在第 5 篇会详细展开,现在只需要记住这个保命用法。

实战:学生选课系统

把前面学到的所有操作串起来,完整地走一遍"建库 → 建表 → 插数据 → 查询"的流程。

第一轮:基础查询

问题 1:查所有课程及学分

sql 复制代码
SELECT name, credit FROM courses;

问题 2:查选了高等数学(course_id=1)的学生的成绩,按分数降序

sql 复制代码
SELECT student_id, score
FROM enrollments
WHERE course_id = 1
ORDER BY score DESC;

问题 3:找出还没有成绩的选课记录

sql 复制代码
SELECT * FROM enrollments WHERE score IS NULL;

注意:判断 NULL 不能用 =!=,必须用 IS NULLIS NOT NULLNULL 在 SQL 里的含义是"未知",任何值与 NULL 比较的结果也是 NULL(不是 true 也不是 false),所以 score = NULL 永远不成立。

第二轮:稍微进阶一点

问题 4:查每门课的平均分

sql 复制代码
SELECT course_id, AVG(score) AS avg_score
FROM enrollments
WHERE score IS NOT NULL
GROUP BY course_id;

AVG() 是聚合函数,AS avg_score 给计算结果起个别名,GROUP BY course_id 表示按课程分组计算。

问题 5:查平均分大于 80 的课程

sql 复制代码
SELECT course_id, AVG(score) AS avg_score
FROM enrollments
WHERE score IS NOT NULL
GROUP BY course_id
HAVING avg_score > 80;

WHEREHAVING 的区别:WHERE 在分组之前 过滤行,HAVING 在分组之后 过滤组。这里用 HAVING 是因为我们要对 AVG(score) 的结果做筛选,而 AVG 的结果在分组之后才有。

问题 6:查张三选了哪些课(课程名、成绩)

sql 复制代码
SELECT c.name, e.score
FROM enrollments e
JOIN courses c ON e.course_id = c.id
WHERE e.student_id = 1;

这是你写下的第一条 JOIN 语句。它的逻辑是:把 enrollments 表和 courses 表按 course_id = id 的条件拼在一起,这样选课记录里就能直接带上课程名称。

这些 SQL 之间的关系

#mermaid-svg-TXDDqgMM1AGgXIMq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TXDDqgMM1AGgXIMq .error-icon{fill:#552222;}#mermaid-svg-TXDDqgMM1AGgXIMq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TXDDqgMM1AGgXIMq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TXDDqgMM1AGgXIMq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TXDDqgMM1AGgXIMq .marker.cross{stroke:#333333;}#mermaid-svg-TXDDqgMM1AGgXIMq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TXDDqgMM1AGgXIMq p{margin:0;}#mermaid-svg-TXDDqgMM1AGgXIMq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster-label text{fill:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster-label span{color:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster-label span p{background-color:transparent;}#mermaid-svg-TXDDqgMM1AGgXIMq .label text,#mermaid-svg-TXDDqgMM1AGgXIMq span{fill:#333;color:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq .node rect,#mermaid-svg-TXDDqgMM1AGgXIMq .node circle,#mermaid-svg-TXDDqgMM1AGgXIMq .node ellipse,#mermaid-svg-TXDDqgMM1AGgXIMq .node polygon,#mermaid-svg-TXDDqgMM1AGgXIMq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TXDDqgMM1AGgXIMq .rough-node .label text,#mermaid-svg-TXDDqgMM1AGgXIMq .node .label text,#mermaid-svg-TXDDqgMM1AGgXIMq .image-shape .label,#mermaid-svg-TXDDqgMM1AGgXIMq .icon-shape .label{text-anchor:middle;}#mermaid-svg-TXDDqgMM1AGgXIMq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TXDDqgMM1AGgXIMq .rough-node .label,#mermaid-svg-TXDDqgMM1AGgXIMq .node .label,#mermaid-svg-TXDDqgMM1AGgXIMq .image-shape .label,#mermaid-svg-TXDDqgMM1AGgXIMq .icon-shape .label{text-align:center;}#mermaid-svg-TXDDqgMM1AGgXIMq .node.clickable{cursor:pointer;}#mermaid-svg-TXDDqgMM1AGgXIMq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TXDDqgMM1AGgXIMq .arrowheadPath{fill:#333333;}#mermaid-svg-TXDDqgMM1AGgXIMq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TXDDqgMM1AGgXIMq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TXDDqgMM1AGgXIMq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXDDqgMM1AGgXIMq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TXDDqgMM1AGgXIMq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXDDqgMM1AGgXIMq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster text{fill:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq .cluster span{color:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TXDDqgMM1AGgXIMq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TXDDqgMM1AGgXIMq rect.text{fill:none;stroke-width:0;}#mermaid-svg-TXDDqgMM1AGgXIMq .icon-shape,#mermaid-svg-TXDDqgMM1AGgXIMq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXDDqgMM1AGgXIMq .icon-shape p,#mermaid-svg-TXDDqgMM1AGgXIMq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TXDDqgMM1AGgXIMq .icon-shape .label rect,#mermaid-svg-TXDDqgMM1AGgXIMq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXDDqgMM1AGgXIMq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TXDDqgMM1AGgXIMq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TXDDqgMM1AGgXIMq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CREATE DATABASE school
CREATE TABLE students / courses / enrollments
INSERT INTO 填充数据
SELECT 基础查询
SELECT + WHERE 条件过滤
SELECT + GROUP BY 聚合统计
JOIN 联表查询
UPDATE / DELETE 修改与删除

从建库到 JOIN,SQL 的学习路径就是一条逐步"组合"的路:先会操作单表,再学会过滤和排序,最后把多张表拼起来。

常见错误速查

错误信息 原因 解决
Table 'xxx' already exists 重复建表 DROP TABLE IF EXISTS xxx; 再 CREATE
Unknown column 'xxx' in 'field list' 列名拼写错误 DESCRIBE 表名; 核对列名
Cannot add or update a child row: a foreign key constraint fails 外键引用了不存在的 ID 确保被引用行的主键确实存在
Duplicate entry 'xxx' for key 'email' UNIQUE 约束冲突 改用不重复的值,或确认是否真的只是想 UPDATE
Column count doesn't match value count INSERT 时列数和值数不一致 检查 VALUES 里是不是少写了或多写了

总结

本文从零开始,用"学生选课系统"这一贯穿全系列的实例,带你完成了 MySQL 入门的第一步:

  • 数据库是专门管理数据的独立程序,解决了文件存储无法处理的并发、一致性、查询效率问题
  • 关系型数据库的核心模型是 ,表与表之间通过主键和外键建立关系
  • INTVARCHARDATETIMEDECIMAL 覆盖了日常开发 80% 的数据类型需求
  • CREATE TABLE 定义结构,INSERT 写入数据,SELECT 查询数据,UPDATEDELETE 修改和删除------WHERE 子句是后两者的保命符
  • NULLIS NULL 判断,别用 =
  • JOIN 是多表查询的核心机制,下一篇文章将深入展开

掌握了本文的内容,你已经能独立完成"建库建表 → 插入数据 → 条件查询 → 简单聚合统计 → 两表联查"的完整流程。这些是后续所有进阶知识------索引、事务、ORM------的地基。

下一篇文章将深入 SQL 查询:多表 JOIN 的各种姿势、子查询的嵌套逻辑、内置函数的实战用法,以及如何用一条 SQL 完成之前需要多次查询才能实现的分析任务。同一个"学生选课系统",你将能用复杂查询挖掘出更多信息。

相关推荐
Elastic 中国社区官方博客2 小时前
如何比较两个 Elasticsearch 索引并找出缺失的文档
大数据·运维·数据库·elasticsearch·搜索引擎
DLYSB_2 小时前
生命通道:如何用 HIS 医疗系统直连网络声光终端,打造“零延误”的危急值响应网关?
java·网络·数据库·报警灯
儒雅的大叔2 小时前
MySQL数据库InnoDB数据恢复工具使用总结
数据库·mysql·adb
观远数据3 小时前
ChatBI选型对比:从意图识别到SQL修复,六个维度打分决定是否值得投产
数据库·人工智能·sql
嘉&年华3 小时前
【第008篇】通过dexp和dimp命令导出和导入dmp文件(适用于达梦数据库)
数据库·sql
哥是强混4 小时前
Means:基于 .NET 10 打造的开源自部署 S3 兼容对象存储服务
网络·数据库·.net
ffqws_5 小时前
redis面试:如何保证双写一致
数据库·redis·缓存
水无痕simon5 小时前
6 数据库同义词
数据库
杜子不疼.5 小时前
【Qt初识】信号槽(三):机制意义、断开连接与 Lambda 表达式
开发语言·数据库·qt