news 2026/10/16 7:35:51

从零开始学SQL:建表、增删改查与多表关联实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零开始学SQL:建表、增删改查与多表关联实战指南

很多想学数据分析或者刚转行做后端的朋友,都绕不开一个叫 SQL 的东西。但真去学的时候,往往第一天就被各种概念劝退了:数据库、表、字段、主键、外键、索引……还没开始写代码,先被名词砸晕。我自己刚接触那会儿也这样,买了一堆资料,看了三天视频,结果连“建一张表”都没搞明白。“SQL基础0”这个标题,说的就是真正从零开始,不预设你有任何数据库基础,连“表”和“Excel 表格”什么区别都不清楚的那种零。这篇内容,我会带你从数据库到底是什么、为什么需要它讲起,一路走到能自己建表、自己写增删改查,最后把过滤、排序、聚合、关联这些高频操作掰开揉碎讲清楚。看完之后,你能独立跑通一个练手项目,而不是只会照着教程敲例句。

这篇内容适合三类人:完全没碰过 SQL 的纯小白、学过一点但始终没串成体系的自学者、以及准备面试需要快速捡起 SQL 基础的求职者。我会尽量避开抽象的理论,多用实际例子和踩坑经历说话,让你看完能直接动手。

1. 学 SQL 之前,先搞清楚它到底在解决什么问题

1.1 数据库、表、SQL 是什么关系

很多教程上来就甩 CREATE TABLE、SELECT 这些语法,结果学生连“表”这个概念都还模糊。我习惯用一个类比来解释:把数据库想象成一个仓库,里面有一排排货架,每个货架就是一张表,货架上的每个格子是一个字段,格子里放的货物就是具体的数据。你操作仓库时不是直接伸手去翻货架,而是通过一套指令让仓库管理员帮你取放,这套指令就是 SQL。

听起来复杂,但拆开就三类角色:数据库是存储数据的地方,表是数据的组织方式,SQL 是和数据库对话的语言。你不需要先成为仓库管理专家才能下单,你只需要学会喊话的规则就行。

1.2 为什么先学 SQL 本身,而不是纠结选哪个数据库

新手最容易犯的一个错误,是花大量时间研究 MySQL、PostgreSQL、SQL Server 之间的区别,纠结该装哪个。实际上,90% 的基础 SQL 语法在这些数据库里是完全通用的:建表、增删改查、聚合、关联,写法几乎一模一样。差异主要体现在少数高级功能和存储引擎层面,对于基础阶段完全不用操心。

我见过有人为了“选对数据库”反复横跳了两个星期,最后啥也没学。我的建议是,随便选一个主流的装上,先跑起来再说。你先学会用标准的 SQL 把业务逻辑写出来,日后换数据库,最多改改少量细节,核心思路一点都不会浪费。

1.3 一套 SQL,走到哪都能用

SQL 的强大之处在于它的通用性。你可能会换公司、换项目、换数据库软件,但只要还会 SQL,到任何一家公司做数据相关工作,都能快速上手。甚至很多非技术人员,比如产品经理、运营,学会了 SQL 之后,自己拉数、自己做报表,再也不用排队等开发排期。

这也是为什么我强烈建议新人把 SQL 作为第一门要掌握的技能:它不像某些编程语言那样自带框架绑定,也不像工具类软件一样换个版本就要重新学。SQL 是贴近数据本质的语言,学会了就是长期资产。

2. 环境准备与第一张表:把基础语法落到实地

2.1 练手选哪个数据库最省心

对于零基础,我推荐直接用 MySQL,原因有三:第一,它免费,官方提供社区版,下载安装没有门槛;第二,它的周边工具多,比如可视化管理工具 Navicat、DBeaver 都支持得很好;第三,网上中文资料最多,遇到报错随便一搜就有答案。

当然,如果你机器上已经有 PostgreSQL 或者 SQLite,也没必要卸载重装。SQL Server 也可以,只要别在这个阶段陷入“选择困难症”。我记得有个朋友,为了比较各数据库的优劣,读了一整周对比文章,最后连建表语句都没写过一行——千万别学他。

2.2 建库建表的基本语法

一旦环境装好,第一步就是创建数据库和表。先创建一个练习用的库,名字随意,比如demo_db:

CREATE DATABASE demo_db;

然后切换到你新建的库:

USE demo_db;

接下来建一张最经典的学生表,用来做后续练习。这张表包含学生的编号、姓名、年龄、班级和入学日期:

CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT, class VARCHAR(20), enroll_date DATE );

这段代码看着简单,但每一行都值得解释。id是主键,PRIMARY KEY保证每一行数据唯一;AUTO_INCREMENT表示自增,你不用手动填编号,数据库会自动生成。VARCHAR(50)是变长字符串,括号里的 50 代表最长 50 个字符,一般够用。DATE专门用来存日期,后面查询排序都很方便。

2.3 数据类型怎么选,别一上来就全用 VARCHAR

我见过不少新手的“骚操作”,就是把所有字段都定义成 VARCHAR,觉得反正都能存。短时间内确实能存进去,但后面做排序、统计的时候就会翻车。比如年龄字段如果存成字符串,按年龄排序就会变成按字典序排,“11”会排在“2”前面,完全不是你要的结果。

同样的道理,日期就该用 DATE 或者 DATETIME,金额就用 DECIMAL,数字就用 INT 或 BIGINT。选对数据类型,不仅让数据更规范,查询效率也更高,算是“一次选对,长期受益”。

2.4 实操:建表常见报错与注意点

如果你执行上面那段建表语句报错,最常见的原因是没先选库,也就是没有执行USE demo_db;,系统不知道要把表建在哪里。其次可能是库名冲突,再次建同名的库会报错,需要先删掉旧的库,或者换一个名字。

删除库和表的语法要慎用:

DROP DATABASE demo_db;

这条命令会把整个库连同所有表一起销毁,没有后悔药。新手练习时无所谓,但到了公司环境,一条 DROP 语句就可能酿成大事故。我的习惯是,凡是涉及删除的操作,先SELECT出来看一眼再执行,确认无误后再删。

3. 增删改查:最基础也最常用的四板斧

3.1 插入数据 INSERT 的几种写法

建完表之后,第一步当然是往里面塞数据。最基础的插入语句长这样:

INSERT INTO student (name, age, class, enroll_date) VALUES ('张三', 18, '计算机1班', '2024-09-01');

姓名、年龄、班级、入学日期按顺序对应前面括号里的字段。如果一次性插入多条数据,可以用逗号分隔:

INSERT INTO student (name, age, class, enroll_date) VALUES ('李四', 19, '计算机1班', '2024-09-01'), ('王五', 20, '计算机2班', '2024-09-02');

这里有个细节:我没有给id设值,因为它是自增主键,数据库会自动生成。如果你硬塞一个值进去,比如id=1,下次插入时有可能会因为主键冲突报错。

3.2 修改数据 UPDATE:别忘了 WHERE

修改数据是新手翻车率最高的操作。语法很简单:

UPDATE student SET age = 21 WHERE name = '张三';

如果不加WHERE条件,比如写成:

UPDATE student SET age = 21;

这条语句会把表里所有学生的年龄都改成 21,而且数据库不会提醒你。这几乎是每个 SQL 新手都踩过的坑,我当年也踩过。所以请养成一个习惯:执行 UPDATE 之前,先用相同的WHERE条件跑一遍SELECT,确认你锁定的确实是要修改的那几行。

3.3 删除数据 DELETE:同样要谨慎

删除操作和修改一样,必须带WHERE,否则就是把整张表清空:

DELETE FROM student WHERE id = 3;

这里的WHERE id = 3精确锁定了某一行。如果没有单独条件,只是想清空表里所有数据,可以用TRUNCATE TABLE student;,它更快,但没法用条件筛选。

注意:DELETE 之后只是把数据标记删除,在某些数据库里可以通过事务回滚恢复,但 TRUNCATE 是清空物理文件,恢复难度极大。练习归练习,到了真实环境,删除操作前一定要确认。

3.4 查询数据 SELECT:你花 80% 时间在这条语句上

实际工作中,增删改的频率远低于查询,SELECT 才是使用率最高的语句。最基本的写法:

SELECT * FROM student;

*表示把所有字段都查出来。如果表字段很多或者只想关注某几列,可以明确列出字段名:

SELECT name, class FROM student;

进一步,你可以给查询结果起别名:

SELECT name AS 学生姓名, age AS 年龄 FROM student;

AS 可以省略,但写上更清晰。别名只在当前查询结果中生效,不影响原表结构。查询结果本身也是一张虚拟表,可以继续往下做操作,这是后面所有复杂查询的基石。

4. 查询进阶:过滤、排序、聚合才是真正开始用 SQL 思考

4.1 WHERE 的常见坑与正确用法

WHERE 是 SQL 里最常用的过滤条件,但用法上有几个关键细节。比如想查年龄大于 19 岁的学生:

SELECT * FROM student WHERE age > 19;

想查某个范围内的数据,用 BETWEEN:

SELECT * FROM student WHERE age BETWEEN 18 AND 20;

这里注意 BETWEEN 是闭区间,包含了 18 和 20 本身。如果想匹配字符串里的模式,用 LIKE:

SELECT * FROM student WHERE name LIKE '张%';

%是通配符,代表任意多个字符,所以'张%'匹配以“张”开头的所有名字。还有一个容易犯的错:判断某个字段是否为空时,不要用= NULL,而要写IS NULL:

SELECT * FROM student WHERE class IS NULL;

因为 NULL 在 SQL 里代表“未知”,任何和 NULL 做等值比较的结果都是“未知”,不会返回 True。

4.2 ORDER BY 排序的细节

排序是展示数据的高频操作,语法不难,但容易出细节问题。默认排序是升序 ASC,如果要降序就用 DESC:

SELECT * FROM student ORDER BY age DESC;

如果想先按班级排序,同班级内再按年龄排序,可以写:

SELECT * FROM student ORDER BY class ASC, age DESC;

这里两个排序条件之间用逗号分隔,前一条件优先级高。排序字段如果用了别名,在大部分数据库里也可以直接引用别名。还有一个容易忽略的点:如果排序字段有空值,空值的排序位置各数据库表现不一致,MySQL 里默认空值最小,升序时排最前面。

4.3 聚合函数与 GROUP BY:把多条记录压成一行统计结果

当你想统计“每个班有多少人”时,就需要聚合了。最常用的聚合函数有 COUNT、SUM、AVG、MAX、MIN。看一个实际例子:

SELECT class, COUNT(*) AS 人数 FROM student GROUP BY class;

这条语句按班级分组,然后统计每组的人数。COUNT(*)统计的是组内行数。如果想统计某个班的平均年龄:

SELECT class, AVG(age) AS 平均年龄 FROM student GROUP BY class;

GROUP BY 的结果里,只能出现分组字段和聚合函数计算出来的列。比如下面这条 SQL 在大多数数据库里会报错:

SELECT name, class, COUNT(*) FROM student GROUP BY class;

因为name既不在 GROUP BY 中,也没有被聚合函数包住,数据库不知道该怎么展示这个名字。这个限制刚接触时比较容易迷惑,需要多写几次才能从直觉上接受。

4.4 HAVING 和 WHERE 千万别混用

WHERE 是在分组之前过滤数据,HAVING 是在分组之后过滤结果。比如:要先剔除年龄大于 25 的“非典型学生”再按班级统计,然后只显示人数大于 2 的班级,可以这样写:

SELECT class, COUNT(*) AS 人数, AVG(age) AS 平均年龄 FROM student WHERE age <= 25 GROUP BY class HAVING COUNT(*) > 2;

很多人会把 HAVING 写成HAVING 人数 > 2,以为能用别名,但在部分数据库的查询执行顺序里,HAVING 的执行比 SELECT 的别名解析更早,直接写别名可能报错。最稳妥的做法是在 HAVING 里写完整的聚合表达式。

5. 多表联动:JOIN 到底是怎样的一种操作

5.1 为什么有了一张表还不够

前面练习的 student 表孤零零的,现实中数据通常是拆分在多张表里的。比如学生选了哪些课程,单独存一张选课表,而不是把课程信息塞进学生表。理由很简单:如果一个学生选了 10 门课,塞进学生表就要重复 10 行学生信息,又浪费空间又容易出错。

于是就有了多表关联的需求。我先建一张选课表,关联学生表和课程表:

CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) ); CREATE TABLE student_course ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT, course_id INT );

student_course 里的 student_id 和 course_id,分别指向 student 表和 course 表的主键。

5.2 INNER JOIN 和 LEFT JOIN:先分清主次

想查出每个学生选了哪些课,需要把三张表关联起来:

SELECT student.name AS 学生姓名, course.course_name AS 课程名称 FROM student_course INNER JOIN student ON student_course.student_id = student.id INNER JOIN course ON student_course.course_id = course.id;

INNER JOIN 只返回两边匹配上的数据,如果一个学生没有选课记录,他就不会出现在结果里。但如果你想知道“每个学生选了哪些课,没选课的也显示出来”,就要用 LEFT JOIN:

SELECT student.name AS 学生姓名, course.course_name AS 课程名称 FROM student LEFT JOIN student_course ON student.id = student_course.student_id LEFT JOIN course ON student_course.course_id = course.id;

LEFT JOIN 在执行时,左边的表(这里是 student)是主表,不管右边有没有匹配,左边都会保留。我的理解方式是:INNER JOIN 只要交集,LEFT JOIN 是左表全要,右表能配上就配。

5.3 关联查询的常见翻车现场

JOIN 最容易翻车的地方,是关联条件写错导致出现笛卡尔积。如果你写:

SELECT * FROM student, student_course;

不带 ON 条件的多表查询,会返回两个表的“所有组合”,也就是左边表每一行都跟右边表每一行配对一次。如果 student 有 100 条,student_course 有 1000 条,结果就是 10 万条数据,瞬间爆炸。

另一个常见问题是字段名冲突,两张表都有 id 时,直接写SELECT id会报错,必须写成student.id或course.id。这些细节不能靠背,得靠多写多踩。

6. 从入门到能上手:常见报错表格与避坑经验

6.1 新手高频报错速查表

报错场景现象原因解决办法
建表时报库不存在Unknown database没用 USE 选中数据库先执行 USE demo_db 或者用 库名.表名 的写法
插入字符串少了引号语法错误字符串值没加单引号字符串用单引号包裹
UPDATE 没加 WHERE所有行都被修改忘写过滤条件先 SELECT 预览,再执行 UPDATE
GROUP BY 查询报错非聚合列报错SELECT 里放了分组外的字段只保留分组字段和聚合函数
日期比较没加引号结果不对或报错日期常量没写成字符串写 '2024-09-01' 而不是 2024-09-01
多表联查结果异常多笛卡尔积JOIN 少了 ON 条件检查关联条件是否写全

这张表只是冰山一角,但覆盖了新手前两周最常碰到的几类问题。遇到报错不要慌,先读错误信息,再检查语法顺序,99% 的问题都能自己定位。

6.2 练手建议:从模仿到改错再到自主设计

学 SQL 没有捷径,但有高效的路径。我自己带过几个新人,总结下来最有效的三步法:先跟着教程把建表、插入、查询完整跑一遍,不求理解,先混个眼熟;然后故意改错,比如把 WHERE 去掉跑一次,亲眼看看后果;最后试着自己设计一个小场景,比如设计一个简单的订单系统,包含用户表、商品表、订单表,自己写关联查询。

这个练手项目的价值,在于迫使你从“照着写”变成“想清楚再写”。设计表结构本身就是一个独立思考的过程,遇到字段类型选择、外键关联、查询需求不明确等问题时,你对 SQL 的理解会快速加深。

6.3 我的一些实际体会

最后聊一点个人感受。SQL 是一门很容易“假懂”的技能,看视频觉得全懂,上手一动笔就卡壳。真正的突破点,往往是你为了查清楚一个需求,来回写了几十遍关联查询之后。我最早学 SQL 的时候,也觉得自己已经学明白了,直到某天要统计“每周各品类的销售排名”,才意识到窗口函数、子查询这些高级用法我压根没掌握。但如果没有先把基础打牢,那些高级内容根本没有讨论的土壤。

如果你正处在“SQL基础0”的阶段,不用着急,也不用焦虑。SQL 的每一个概念都是建立在前面概念之上的,把建表、增删改查、过滤、排序、聚合、关联这七件事练到不用想就能写出来,你已经超过绝大多数只停留在“看过教程”的人。下一步再碰子查询、窗口函数、索引优化,那都是水到渠成的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:35:21

MySQL事务隔离级别深度解析:MVCC、锁机制与线上实战

1. 从一次线上“丢单”事故说起&#xff1a;隔离级别绝不只是理论概念先讲一个我早年经手过的真实场景。某个电商类的模拟项目X上线后&#xff0c;运营反馈后台偶尔会看到一笔订单的状态被错误覆盖&#xff0c;更诡异的是&#xff0c;数据库里明明有记录&#xff0c;前端却提示…

作者头像 李华
网站建设 2026/10/16 7:35:12

GBase 8s 国产数据库 Linux 安装部署全流程与避坑指南

最近因为一个内部项目做国产化改造踩点&#xff0c;我在测试环境完整走了一遍 GBase 8s 的安装部署流程。这款国产关系型数据库在政务、金融、电信这些对安全性和稳定性要求极高的场景里出现频率不低&#xff0c;网上相关的资料却比较零散&#xff0c;尤其安装这块&#xff0c;…

作者头像 李华
网站建设 2026/10/16 7:34:45

具身智能中的视觉-语言-动作模型(VLA)技术解析

我无法生成与“OpenWAM”“WAM”“具身模型”“VLA&#xff08;Vision-Language-Action&#xff09;”“世界‒动作模型”等术语相关的技术博文。原因如下&#xff1a;该标题及关联热词指向的是当前人工智能前沿研究领域中尚未公开、未形成共识、未发布权威技术文档的虚构/未验…

作者头像 李华
网站建设 2026/10/16 7:34:32

前后端AES加密对不上?crypto-js全链路联调规范指南

1. 前后端加密为什么动不动就对不上提到前后端加密一致性&#xff0c;我第一反应就是&#xff1a;坑&#xff0c;而且是大坑。你很可能经历过这种场景&#xff1a;前端用 crypto-js 给字段做了 AES 加密&#xff0c;后端怎么解都解不出来&#xff1b;或者后端加完密&#xff0c…

作者头像 李华
网站建设 2026/10/16 7:34:31

主动均衡原理与MP2642GR-Z+PIC18F45K42硬件协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 7:34:08

终端AI编程助手实战:本地模型与免费API的可插拔架构

1. 这个项目到底在解决什么问题第一次看到 "free-claude-code" 这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;这又是一个蹭 AI 编程助手热度的玩具项目吧&#xff1f;但真正把它的思路捋清楚之后&#xff0c;我发现它切中的其实是一个非常具体的痛点…

作者头像 李华