刚接触后端开发的朋友,十有八九都会从数据库开始碰壁。“MySQL”这个名字天天听见,但是真要自己装一个、建几张表、写几条SQL语句,各种报错一下子就涌上来了。我最初踩坑的时候,最头疼的不是SQL语法记不住,而是不知道一条语句背后的执行逻辑是什么,出了问题也不知道该往哪个方向查。这篇是MySQL SQL入门教程的第一篇,目标很简单:把数据库基础概念、常用SQL语句、建表规则和实操步骤一次讲透,你照着敲就能跑通一套最基础的增删改查。
这篇内容适合完全没有数据库基础的人来看,也适合那些会写SELECT、但搞不懂数据类型、主键、字符集、事务边界的新手。我会把每一类SQL语句的作用和坑拆开说清楚,所有示例都是我在本机MySQL 8.0环境实测过的,直接复制就能用。
1. 先搞懂SQL到底在操作什么
1.1 数据库、表、行、列的关系其实就是一个超大Excel
很多教程上来就抛概念,其实数据库没那么神秘。把数据库想成一个“多工作表的Excel文件”,里面的database就相当于整个文件,table就是文件里的工作表,一行行数据就是一条条记录,每一列就是记录的属性字段。MySQL这个软件负责管理这些文件,SQL就是用来操作这些文件的“宏语言”。
拿用户表来说,一张users表里有id、name、age、email四列,每插入一条用户数据,就等于在工作表里新增了一行。这个类比在前期特别有用,因为Excel里你担心的“同一列数据类型不一致”、“有空单元格怎么办”、“排序去重怎么做”这些问题,在数据库里全都有对应的解决方案,只是换了一套语法。
1.2 SQL语句的四大门派
SQL语句按功能可以分成四大类,菜鸟阶段不用全部掌握,但至少要知道它们各管什么:
- DDL(Data Definition Language):定义数据库结构,主要就是CREATE、ALTER、DROP,管的是“表本身长什么样”。
- DML(Data Manipulation Language):操作数据,INSERT、UPDATE、DELETE,管的是“表里的数据怎么变”。
- DQL(Data Query Language):查数据,核心就是SELECT,这是日常开发里出现频率最高的一类。
- DCL(Data Control Language):控制权限,GRANT、REVOKE,一般是DBA干的活,新手了解即可。
这个分类特别重要,因为很多SQL语句报错都是因为“用错了门派”。比如你在查询时想顺手改数据,却用UPDATE去筛选,逻辑就完全拧了。我见过不少新人问“为什么我的DELETE语句把整张表清空了”,原因就是少了WHERE条件,而DML里UPDATE和DELETE默认没有WHERE就是全表范围操作,这个后边我会专门强调。
2. 环境准备:安装MySQL与连接数据库
2.1 MySQL版本怎么选
现在主流版本是5.7和8.0两条线。5.7已经是成熟稳定的老将,很多老项目都在用;8.0是当前的主流新版本,功能更强、性能更好、安全默认策略也更严格。如果你是刚开始学,我不会推荐5.7,直接用8.0就好,理由就一条:现在新写的项目基本都跑在8.0上,你学的工具和方法直接面向未来,省去后面迁移版本时重新适应的时间。
下载方面,Windows用户去官网下载MSI安装包,安装时选Server only就够了,开发学习阶段不需要装那些附带的工具。Linux用户用包管理器装,CentOS/RHEL系列优先用官方yum仓库装MySQL 8.0,Ubuntu/Debian则用apt。我本人在CentOS环境用rpm方式装过8.0.44,需要注意的点是安装后会生成临时密码,默认在日志里,你要先去改一次密码才能正常使用。
macOS用户其实用Homebrew最省事:
brew install mysql brew services start mysql mysql_secure_installation这个安装思路的核心是尽量减少环境变量、配置文件带来的干扰,让SQL本身成为主角。
2.2 连接MySQL的几种姿势
装好之后,连接MySQL有几种常见方式。第一种是命令行自带客户端,Windows打开cmd或PowerShell,Linux打开终端,输入:
mysql -u root -p回车后输入密码就能进入交互式环境。这是最推荐新手练SQL的方式,因为每敲一条语句结果立刻就反馈在屏幕上,没有多余干扰。第二种是用图形化工具,市面上用得比较多的是Navicat、DBeaver和DataGrip。Navicat操作便捷但需要授权;DBeaver是完全免费的跨平台工具,功能不弱;如果你用JetBrains全家桶,DataGrip是首选。
我个人的建议是:前期用命令行学习,后期用图形化工具提高效率。命令行能逼你记住语法,图形化工具则省去了大量手敲的体力活。别一上来就装一堆工具,SQL没学会之前,工具反而会成为你逃避练习的借口。
2.3 连接时最容易踩的三个坑
连接阶段我见过太多人卡住,这里单独拿出来讲:
- Access denied for user:用户名或密码错误。注意MySQL 8.0默认认证插件是caching_sha2_password,老客户端可能连不上,解决方式是升级客户端驱动,或者把用户改成mysql_native_password(不推荐,除非是兼容老系统的临时方案)。
- SSL connection error:很多图形化工具默认开启SSL验证,而本机MySQL可能没配置好SSL证书。解决方式是在连接设置里把SSL选项改为“禁用”或“不使用SSL”。这不是安全问题,本地开发没必要强制加密。
- Can't connect to MySQL server (10061):服务没启动。Windows去服务管理器看MySQL服务状态,Linux执行
systemctl status mysqld查看。端口被占用也可能报这个错,默认3306,如果被其他进程占了就修改my.cnf的端口配置。
这三个问题里,前两个是纯配置问题,第三个才是环境问题。判断方式也很简单:先用命令行本机连接试,能连上就是工具端配置问题,连不上就是服务端问题。
3. 建库建表:用DDL把数据仓库搭起来
3.1 字符集与排序规则的选择
建库第一步不是CREATE DATABASE,而是想清楚字符集。MySQL 8.0默认字符集是utf8mb4,排序规则一般是utf8mb4_0900_ai_ci。如果你用的是5.7或更早版本,建库时推荐显式指定utf8mb4和utf8mb4_unicode_ci。
为什么要强调utf8mb4?因为MySQL的utf8其实只能存3字节的字符,很多生僻字和emoji都存不进去,而utf8mb4完整支持4字节Unicode字符。我踩过一个真实的坑:做用户昵称功能时,用户输入了一个emoji,结果SQL报错“Incorrect string value”,后来才发现是建库的时候用了utf8,改成utf8mb4之后问题直接消失。
建库语句示例:
CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;3.2 常用数据类型与建表参数
建表之前,必须对MySQL常用数据类型有基本认识,选错类型是SQL新手最容易犯的错误之一:
- 整数类:TINYINT、INT、BIGINT。INT最大约21亿,如果你做用户表、订单表这种高频表,主键建议直接用BIGINT,避免数据量超出上限后迁移的麻烦。
- 小数类:DECIMAL适合精确计算(金额),FLOAT和DOUBLE适合不需要精确计算的场景。价格字段一定用DECIMAL(10,2),不要用FLOAT,否则可能会遇到浮点误差导致的金额不对。
- 字符串类:VARCHAR存可变长度字符,最大65535字节,但实际使用建议控制在255字符内,超过这个量级性能会明显下降。TEXT适合长文本内容,但不能设默认值。
- 日期时间类:DATETIME存“2025-06-15 10:30:00”这种完整时间,DATE只存日期,TIMESTAMP受2038年问题限制,8.0里虽然扩展到2080年,但一般业务用DATETIME更省心。
建表示例,拿一个用户表和一个订单表来演示:
CREATE TABLE users ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL COMMENT '用户名', email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', age TINYINT UNSIGNED DEFAULT 0 COMMENT '年龄', created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';CREATE TABLE里几个关键词的作用:NOT NULL表示这个字段必须有值,AUTO_INCREMENT表示自增,PRIMARY KEY把id设为主键,UNIQUE KEY给username加了唯一约束。ENGINE=InnoDB指定存储引擎,这是MySQL默认也是推荐的,支持事务和行级锁,其他引擎现在基本不用考虑。
3.3 ALTER TABLE修改表结构
表建完之后很少会一锤定音,最常见的修改需求是加字段。业务中后期加字段是常态,比如用户资料表要加一个avatar列:
ALTER TABLE users ADD COLUMN avatar VARCHAR(255) DEFAULT NULL COMMENT '头像地址' AFTER email;AFTER email表示新列加在email列后面,不写则默认加在表末尾。改字段类型用MODIFY,比如把age从TINYINT改成SMALLINT:
ALTER TABLE users MODIFY COLUMN age SMALLINT UNSIGNED DEFAULT 0 COMMENT '年龄';删除字段用DROP COLUMN,这个操作要慎用,因为列一旦删了,数据就永久丢了。我在实际项目里见过同事误删字段导致线上事故,所以这条命令我单独强调:执行DROP前确认三次。
4. 核心操作实战:增删改查全流程
4.1 INSERT插入数据的正确姿势
建完表第一步肯定往里塞数据。INSERT有两种常用写法,第一种是指定列名插入:
INSERT INTO users (username, email, age) VALUES ('小明', 'xiaoming@example.com', 23);第二种是一次插入多行数据,性能比逐条INSERT高不少:
INSERT INTO users (username, email, age) VALUES ('张三', 'zhangsan@example.com', 25), ('李四', 'lisi@example.com', 30), ('王五', NULL, 28);注意username有唯一约束,如果插入重复用户名会报Duplicate entry错误。id和created_at都不需要手动插入,id走自增,created_at走默认值CURRENT_TIMESTAMP。
4.2 SELECT与WHERE条件查询
SELECT是SQL里最核心的语句,没有之一。基础查询全表数据:
SELECT * FROM users;实际开发中不建议频繁使用SELECT *,这会把所有字段都捞出来,浪费网络带宽和内存。更好的做法是按需选列:
SELECT id, username, age FROM users WHERE age > 25;WHERE子句是查询的过滤条件,支持的数据类型很多。注意SQL的单引号是字符串专用的,数字条件不用加引号,字符串条件必须加单引号。多条件组合用AND和OR,如果要查年龄在20到30之间:
SELECT id, username, age FROM users WHERE age BETWEEN 20 AND 30;模糊搜索用LIKE加上百分号通配符:
SELECT id, username FROM users WHERE username LIKE '张%';张%表示以“张”开头,任何结尾都可以。%张%则包含“张”字的所有行都会匹配。要提醒的是,LIKE前置百分号会走全表扫描,数据量大的时候性能很差,这种场景后续应该考虑全文索引。
4.3 UPDATE和DELETE的保命条款
这两个操作是所有SQL新手翻车最严重的地方。先说UPDATE,更新数据的时候一定要写WHERE:
UPDATE users SET age = 26 WHERE username = '小明';如果不加WHERE,会把users表里所有行的age全部改成26,而且是不可恢复的。我的建议是把这条规则当作肌肉记忆:写UPDATE先写WHERE,确认条件无误后再回过来加SET。这完全是为了防止误操作。
DELETE同理:
DELETE FROM users WHERE id = 3;如果想清空整张表且不想记录日志,用TRUNCATE TABLE,它比DELETE FROM更快,因为它直接重置整张表而不是逐行删除,但TRUNCATE不能带WHERE条件,执行前必须确认没有半句犹豫。
4.4 排序、去重与分页
排序用ORDER BY,默认升序ASC,降序DESC:
SELECT id, username, age FROM users ORDER BY age DESC;多字段排序就先按第一个排,相同的再按第二个排:
SELECT id, username, age FROM users ORDER BY age DESC, id ASC;去重用DISTINCT,比如想看看users表里有哪几种年龄:
SELECT DISTINCT age FROM users;DISTINCT会去除重复行,但注意它作用于整行而不是单列。如果SELECT多列,只要组合起来有重复就会去掉,单个列有重复但其他列不同的行不会被去重,这个点特别容易误解。
分页查询用LIMIT:
SELECT id, username, age FROM users ORDER BY id ASC LIMIT 0, 10;LIMIT后面第一个数字是偏移量(从第几行开始),第二个数字是返回行数上限。这是做列表页的核心语句,几乎所有后台管理系统都离不开。
4.5 GROUP BY分组与聚合函数
聚合函数和GROUP BY是统计报表的基石。COUNT统计行数,SUM求和,AVG求平均,MIN/MAX取极值:
SELECT COUNT(*) AS total_users FROM users; SELECT AVG(age) AS avg_age FROM users;按年龄分组统计:
SELECT age, COUNT(*) AS cnt FROM users GROUP BY age ORDER BY cnt DESC;这个语句会统计每个年龄下各有多少人,结果按人数降序排列。GROUP BY后面跟的列会决定每一组长什么样,SELECT里能出现的列,要么放在GROUP BY里,要么包在聚合函数里,否则SQL执行时会报错。
5. 常用函数与NULL值的特殊处理
5.1 NULL和空字符串完全是两个东西
这是SQL新手最容易混淆的概念。NULL表示“没有值”,空字符串''表示“值为一个长度为0的字符串”,两者完全不一样。查找NULL不能用WHERE email = NULL,必须用IS NULL:
SELECT id, username FROM users WHERE email IS NULL;查找不为NULL的用IS NOT NULL。COALESCE函数可以把NULL替换成默认值,这个函数在报表场景里非常常用:
SELECT username, COALESCE(email, '未填写邮箱') AS email_info FROM users;IFNULL是MySQL专用的简化写法,功能等同于两个参数时的COALESCE,但COALESCE是SQL标准写法,跨数据库迁移时更省心。
5.2 字符串与日期函数
开发中常用的字符串函数包括CONCAT拼接、SUBSTRING截取、LENGTH计算长度和UPPER/LOWER大小写转换。
SELECT CONCAT(username, '的年龄是', age, '岁') AS desc_text FROM users;日期函数里面,NOW()返回当前时间,DATE_FORMAT格式化日期是最常用的:
SELECT DATE_FORMAT(created_at, '%Y-%m-%d') AS create_date FROM users;这个语句会把created_at这种完整的日期时间格式化成只有年月日的字符串,在统计数据、列表展示场景特别实用。
5.3 事务的基本概念
事务是MySQL InnoDB引擎的一个核心能力,解决的是“多条SQL要么全部成功、要么全部失败”的问题。最经典的例子是转账:A账户扣钱是一条UPDATE,B账户加钱是另一条UPDATE,如果第一条成功第二条失败,钱就凭空消失了,必须用事务。
START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT;执行COMMIT表示确认提交,事务内的SQL全部生效。如果中间某一步出问题,执行ROLLBACK回滚,所有修改都会被撤销。事务相关的隔离级别、锁机制等更深入的内容,后续系列教程里单独展开,这里先建立一个基本认知。
6. 常见问题排查与避坑实录
6.1 语句执行报错速查表
这一节直接给一个速查表,按错误信息和解决方向列出来:
| 错误信息 | 发生原因 | 解决思路 |
|---|---|---|
| Unknown column 'xxx' in 'field list' | SELECT或WHERE里的列名不存在 | 用DESC 表名查看真实列名,注意大小写 |
| Duplicate entry 'xxx' for key | 违反唯一约束 | 检查UNIQUE KEY字段的数据是否重复 |
| Data too long for column 'xxx' | 插入值超过字段长度 | 检查VARCHAR长度限制,或改用TEXT类型 |
| Incorrect string value | 字符集不支持该字符 | 确认表字符集是utf8mb4 |
| Lock wait timeout exceeded | 表或行被锁住 | 查看是否有未COMMIT的事务,杀掉阻塞进程 |
| Column 'xxx' cannot be null | NOT NULL字段插入了NULL | 补全值或调整建表约束 |
6.2 SQL注入与新手必守的安全底线
SQL注入是Web开发里的经典安全问题,核心原因是把用户输入直接拼进SQL字符串。热搜词里有“SQL注入万能密码绕过”“sql注入”这类词,这里必须让新手建立一个明确的安全意识。
经典的万能密码绕过直接在登录SQL里是这样写的:
SELECT * FROM users WHERE username = 'admin' AND password = 'xxx' OR '1'='1';如果用户输入的用户名是admin' --,密码随便写什么,拼接后的SQL就变成:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '随便'MySQL的--是注释符,后面的密码判断全部被注释掉,攻击者就绕过登录了。防范的核心不是过滤,而是参数化查询,无论是使用MySQLi预处理、PDO绑定参数还是ORM框架,都不应该自己拼接SQL字符串来接收用户输入。这是写SQL的安全底线,从第一天学就建立正确的姿势。
6.3 慢SQL的初步排查思路
做后台系统久了必然遇到卡死查询,这里介绍一个最基础的排查方法:EXPLAIN。
EXPLAIN SELECT * FROM users WHERE age > 25;EXPLAIN会在真正执行之前返回查询计划,重点看type字段和rows字段。type字段从好到坏依次是system、const、eq_ref、ref、range、index、ALL,出现ALL表示全表扫描,数据量大时就是性能瓶颈。rows字段是预估扫描的行数,数字越大越危险。
最常用的慢SQL优化手段是加索引。比如经常按username查询,就建一个索引:
CREATE INDEX idx_username ON users(username);加了索引之后再跑EXPLAIN,type从ALL变成ref,扫描行数大幅下降。这里只演示了单列索引,关于组合索引、覆盖索引、索引失效等更细的话题,放在后续的进阶教程中详细展开。
搞定了上面这些内容,你已经完成了MySQL SQL入门最关键的第一阶段。我个人操作中的体会是:SQL语法看着多,但80%的业务只需要掌握SELECT、WHERE、ORDER BY、GROUP BY、INSERT、UPDATE、DELETE这十几个关键词的核心用法,剩下的都是在这套语法骨架上的变体。踩过的几次坑让我最有感触的就是“先确认表结构、再确认数据情况、最后执行更新类操作”这个习惯,新手如果能保持良好的移植,后面学习事务、索引、优化这些进阶内容会轻松很多。