数据库这块内容,我在前面几十天的学习里一直有点发怵。倒不是觉得它难,主要是概念太多,什么关系型非关系型、事务、索引、范式,听着就头大。但到了第36天,我意识到必须正面刚了——因为不管是做爬虫存数据、写Web后端,还是搞数据分析,数据库永远是绕不开的那道坎。今天这篇,我把关系型数据库和MySQL从零到能用的整个链路捋了一遍,配合Python实操,希望能帮跟我一样卡在这里的同学一把。
先说结论:如果你正在学Python,MySQL是你最值得花时间先搞定的数据库,没有之一。它足够主流、资料足够多、坑也基本都被前人踩平了。你不需要一开始就搞懂那些高深的调优和架构,你只需要会三件事:装好它、能用SQL存取数据、能在Python里连上它。能把这三件事做利索,你就已经超过了相当一部分初学者。
1. 为什么Python学习者绕不开关系型数据库
1.1 从文件存储到数据库:这是必经之路
学Python初期,大部分人存数据用的都是文件:JSON、CSV,甚至直接写TXT。我自己在练爬虫的时候就深有体会——爬个几百条数据写到JSON里没问题,但数据一旦上万条,或者需要按条件查、需要频繁更新某几条,文件方案立刻变得极其别扭。
举个例子,你用JSON存了一万个用户信息,现在想找出所有"注册时间在2024年6月之后且积分大于1000"的用户,用Python遍历也能做,但每次查询都要读整个文件、解析、过滤,性能差不说,代码写起来也绕。而用数据库,一条SQL就解决了:
sql复制SELECT * FROM users
WHERE register_time > '2024-06-01' AND points > 1000;
这就是"关系型数据库"存在的核心意义:用结构化的方式组织数据,用标准化的语言(SQL)操作数据。Python负责算,数据库负责存和取,各干各擅长的,这是目前绝大多数Web应用和数据系统的基础架构。
1.2 关系型数据库和NoSQL,为什么先学MySQL
你可能听过NoSQL、MongoDB、Redis这些名词。它们各有各的优势场景,但作为入门,我强烈建议先啃MySQL。原因有三个:
一是关系型数据库的思想是基础。表、主键、外键、索引、事务这些概念,是理解一切数据系统的地基。你把这些搞懂了,以后再看MongoDB、Redis,会发现很多东西是相通的,只是实现方式不同。反过来,如果一上来就学NoSQL,很容易陷入"只知道怎么用、不知道为什么这么设计"的迷茫。
二是MySQL的生态和学习资料最丰富。不管你现在用什么技术栈,Python、Java、PHP、Go,MySQL都是标配之一。遇到问题,搜一下基本都能找到答案。这点在初学阶段极其重要,能省下大量排查问题的时间。
三是市场需求大,面试必问。不管是Python后端岗还是数据分析岗,MySQL几乎都是硬性要求。你现在把它学扎实了,后面不管是做项目还是求职,都是实打实的加分项。所谓"免费python源码大全"里,十个项目有九个要配数据库,配的基本就是MySQL。
这里插一句,MySQL和SQL Server、Oracle、PostgreSQL同属关系型数据库,SQL语法大体相通。你就把MySQL当成一个具体的"数据库软件",SQL是跟它对话的语言。学会了一门,其他的上手都很快。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动笔写SQL之前,先把MySQL装起来
2.1 安装方式:用Docker最省心,但也别排斥手动装
MySQL的安装是很多新手的第一道坎,尤其是Windows用户。我见过太多人卡在环境变量、服务启动、账号密码这些地方。这里我推荐两条路,你根据自己情况选:
路线一:Docker安装(推荐,最省心)
如果你已经装了Docker,那用Docker装MySQL是最干净的方式,不用怕污染系统环境,出问题删了容器重来就行。一行命令搞定:
bash复制docker run --name mysql-study \
-e MYSQL_ROOT_PASSWORD=你的密码 \
-p 3306:3306 \
-d mysql:8.0
跑起来之后,用docker ps看容器是否在运行。如果端口被占,改宿主机的映射端口就行,比如-p 3307:3306。这里说一下,容器里的MySQL默认端口是3306,-p 3307:3306的意思是把宿主机的3307端口映射到容器的3306端口,这样你连接的时候要用3307,写Python代码连接时也要对应改成3307。
路线二:Windows直接安装
去官网下载MySQL Installer,选"Developer Default"或者只选"MySQL Server",一路Next。有一个非常多人踩的坑是——设置root密码那一页,默认的认证方式是caching_sha2_password,如果你用的Python库是老版本的pymysql,连接时就会报Authentication plugin 'caching_sha2_password' cannot be loaded的错。解决办法有两个:
- 把认证方式改成
mysql_native_password; - 升级你的pymysql到最新版。
我个人建议选第二个方案,更省事。老版本的pymysql对新版MySQL的支持确实不行,升级到最新版基本就能解决。
还有一个更隐蔽的坑:MySQL 8.0+ 默认字符集虽然已经是utf8mb4,但如果你在创建数据库的时候不显式指定,某些场景下还是可能出现中文乱码。养成好习惯,建库时把字符集写清楚,后面能少很多麻烦。
2.2 验证安装结果
装完之后,打开命令行(Windows的cmd或者PowerShell),输入:
bash复制mysql -u root -p
回车后会提示输入密码,输对了就能进入mysql>的交互界面。看到这个提示符,说明你的MySQL已经跑起来了。输exit退出,这个简单的验证就算通过。
如果你连不上,先确认MySQL服务有没有启动。Windows下可以在"服务"里找"MySQL"相关的服务,Docker的话就用docker ps看容器状态。90%的连接问题都出在服务没起来,这个顺序要先查。
2.3 可视化工具:新手友好型选Workbench
命令行操作虽然很酷,但建表、看数据这种活儿,有个可视化工具效率高得多。官方有个MySQL Workbench,免费、跨平台,新手用起来压力小。安装好之后,填入主机名127.0.0.1、端口3306(或你映射的端口)、用户名root、密码,点连接就能进去了。
Workbench里面可以可视化建库、建表、写SQL、看结果集,比命令行直观太多。我建议的用法是:用Workbench做操作和分析,用命令行做验证。两个都会,后面去服务器上排查问题才不慌——线上环境基本只有命令行可以操作。
3. 关系型数据库的核心概念,用大白话讲透
3.1 表、行、列:跟Excel没什么本质区别
关系型数据库里最核心的存储单位是"表"。你可以把表理解成Excel的一个Sheet:每一列是一个"字段"(field),每一行是一条"记录"(record)。比如一张用户表users,可能会有这几列:id、username、email、register_time。每一行就是一个用户的完整信息。
这里的关键是设计表结构时要提前想清楚有哪些列、每列存什么类型。就好比做Excel表格,你总得先定好表头再填数据对吧?数据库也是这个道理,而且更严格——一旦定好结构,后面想改列名、改类型,代价比Excel大得多。
3.2 主键、外键、索引:三个必须搞懂的概念
主键(Primary Key):用来唯一标识一行数据的字段。比如用户的id,每个用户的id都是唯一的,不会重复。主键有两个核心约束:不能为空、必须唯一。没有主键的表就像没有身份证号的人一样,系统里根本不知道"这一行"和"那一行"的区别。
外键(Foreign Key):用来表达表与表之间关系的字段。比如你有一张订单表orders,里面存了user_id,这个user_id就是外键,它指向users表的id。通过这个字段,你就能知道"这个订单是哪个用户下的"。外键是用来保证数据一致性的:你总不能给一个不存在的用户下订单吧?
索引(Index):用来加速查询的"目录"。你可以把索引理解成字典前面的拼音索引——不建索引,查一个字就要从头翻到尾;建了索引,直接翻到对应页码就行。MySQL里最常用的是B+ Tree索引,在WHERE条件经常用到的字段上加索引,查询速度会有质的提升。但索引不是越多越好,因为每次插入、更新数据时索引也要跟着更新,这是有代价的。
用生活类比来说:主键就是你的身份证号,外键就是你填的"户籍地址"里关联到某个区划代码,索引就是图书馆的检索卡片。这三个概念你能用自己的话解释清楚,面试的时候数据库这块的第一关就算过了。
3.3 关系型与非关系型:时机对了自然懂
还是简单提一嘴,免得大家产生疑惑。关系型数据库(如MySQL)强在"关系"——数据之间有明确的关联,用SQL查询特别灵活。适合需要事务、需要复杂关联查询的场景,比如订单系统、用户系统。
非关系型数据库(NoSQL)则牺牲了一部分关联能力,换取更高的扩展性和更灵活的存储结构。比如MongoDB存的是文档(类似JSON),Redis存的是键值对,适合缓存、会话存储、高并发读写等场景。
初学阶段你不需要深入比较,只需要明白:MySQL这类关系型数据库是所有数据存储的基础课,先把这门课学扎实,后面看其他东西都会有底。
4. SQL基础实操:从建库到增删改查
4.1 建库建表:先把地盘划好
进入MySQL之后,第一步是建库。库(Database)就是一堆表的集合,相当于你电脑里的一个文件夹。命令:
sql复制CREATE DATABASE IF NOT EXISTS demo_db
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
这里有两个参数值得多说两句。utf8mb4是UTF-8的完整实现,支持emoji和生僻字,MySQL里的utf8其实是残缺版,强烈建议直接上utf8mb4。utf8mb4_unicode_ci是排序规则,ci是case insensitive,也就是排序时不分大小写,这是比较常用的选择。
建好库之后,用USE demo_db;切进去,然后建表。我用一个简单的用户表和订单表演示:
sql复制CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
points INT DEFAULT 0,
register_time DATETIME NOT NULL
);
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
created_at DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
建表时有几个点新手容易搞混。第一个是VARCHAR和CHAR的区别:VARCHAR(50)是变长,最多50个字符,存"abc"只占3个字符的空间;CHAR(50)是定长,哪怕存"abc"也占50个字符的空间。绝大多数场景用VARCHAR就够了,不要动辄用很大的长度,50到255已经能覆盖绝大多数需求。
第二个是DECIMAL(10, 2),这是存金额的推荐类型。10表示总位数,2表示小数位数,所以最大可以存到99999999.99。存钱这种精确数值千万不要用FLOAT或DOUBLE,浮点数有精度问题,比如0.1 + 0.2在二进制里表示不精确,存金额会出大问题。
4.2 增删改查(CRUD):最核心的四个操作
CRUD是数据库操作的四件套:增(Create)、查(Read)、改(Update)、删(Delete)。我逐个说,每个都有实际例子。
新增数据(INSERT):
sql复制INSERT INTO users (username, email, points, register_time)
VALUES
('zhangsan', 'zhangsan@example.com', 100, NOW()),
('lisi', 'lisi@example.com', 200, NOW());
这里用NOW()取当前时间,也可以手动指定'2024-06-01 10:00:00'。注意列名要和值一一对应。如果你想插入的数据在某个字段上设置了DEFAULT,那这一列可以不写,让MySQL自动补默认值。
查询数据(SELECT):
sql复制-- 查询所有列
SELECT * FROM users;
-- 只查指定列,并排序
SELECT username, points FROM users
ORDER BY points DESC;
查询是整个SQL里最灵活的部分。WHERE加条件、ORDER BY排序、LIMIT限制条数,这三个是初学者最先要掌握的组合。比如:
sql复制SELECT username, points FROM users
WHERE points >= 100
ORDER BY points DESC
LIMIT 10;
这条SQL查的是积分大于等于100的前10名用户。LIMIT后面还可以跟两个参数,比如LIMIT 10, 20表示跳过10条取20条,这就是分页的基本思路。
更新数据(UPDATE):
sql复制UPDATE users
SET points = points + 50
WHERE username = 'zhangsan';
注意,UPDATE语句千万不能忘记WHERE条件。如果漏了,就会把整张表的所有行都更新,这个事故属于入门级翻车现场,但也真的很多人踩过。写UPDATE和DELETE之前,习惯性先想一下有没有写WHERE。
删除数据(DELETE):
sql复制DELETE FROM users WHERE id = 1;
同样是WHERE条件的生死局。另外,DELETE只是删除数据行,表结构还在;如果你想连表结构一起删掉,用DROP TABLE users;。这两者的区别是:前者是"把文件里的内容清空",后者是"把文件本身也删了"。
4.3 条件查询和聚合:从能用到会用
光会上面这些,已经能应付基本的"数据库增删改查"需求。但要真正做点像样的东西,聚合查询必不可少。什么是聚合?就是对一组数据进行汇总计算,比如数有多少条、求和、求平均值。
sql复制-- 统计用户总数
SELECT COUNT(*) FROM users;
-- 计算所有订单的平均金额
SELECT AVG(amount) FROM orders;
-- 按用户分组统计订单总额,并且只显示总额大于500的用户
SELECT user_id, SUM(amount) AS total_amount
FROM orders
GROUP BY user_id
HAVING total_amount > 500;
GROUP BY用来分组,HAVING用来对分组后的结果做筛选。这里需要区分一下:WHERE是在分组之前过滤行,HAVING是在分组之后过滤组。你用WHERE过滤单个订单,用HAVING过滤"总金额大于500的用户分组",这是两类不同的场景。
还有一个非常常用的排序关键词是ORDER BY,上面已经用了。MySQL排序默认是按升序(ASC)排,要倒序就加DESC。注意,有中文排序需求时,排序结果和字符集、排序规则有关,utf8mb4_unicode_ci的排序规则对中文是按照Unicode编码排的,约等于按拼音排,一般够用。
5. Python操作MySQL:从连接到第一个查询
5.1 安装驱动:pymysql是最亲民的入口
Python要连MySQL,需要先装一个"驱动"库,就是让Python能跟MySQL通信的翻译官。最常用的是PyMySQL,纯Python实现,安装简单,对新手友好。
bash复制pip install pymysql
如果网络不好,可以换国内镜像源:
bash复制pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
也可以用mysql-connector-python,那是Oracle官方出的。两者用起来差不多,但社区里pymysql的资料更多,遇到问题好搜,所以我推荐从pymysql入手。
这里要注意,如果你用的是MySQL 8.0以上,并且pymysql版本过旧(比如0.9.3),连接时会报
Authentication plugin 'caching_sha2_password' cannot be loaded。解决办法就是升级pymysql:pip install --upgrade pymysql。这个报错太经典了,几乎每个用老版本pymysql连MySQL 8.0的人都会遇到。
5.2 写一个完整的连接和查询Demo
先看一个最简单的连接示例:
python复制import pymysql
# 1. 建立连接
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='你的密码',
database='demo_db',
charset='utf8mb4'
)
# 2. 创建游标
cursor = conn.cursor()
# 3. 执行SQL
cursor.execute('SELECT id, username, points FROM users')
rows = cursor.fetchall()
for row in rows:
print(row)
# 4. 关闭游标和连接
cursor.close()
conn.close()
这里有几个细节要特别强调。
第一,charset参数一定要写utf8mb4,如果你建库时用的utf8mb4,Python连接时也匹配,中文才不会乱码。charset和建库时的字符集不一致,是最常见的乱码原因。
第二,fetchall()一次取回所有结果。如果查询结果很大(比如几十万行),建议改用fetchmany(size=1000)分批取,或者直接游标里迭代。一次性全取回内存,数据量大的时候会崩。
第三,连接用完之后要关闭。更优雅的做法是使用上下文管理器(with),这样就算代码中途报错,连接也会被自动关闭:
python复制with pymysql.connect(...) as conn:
with conn.cursor() as cursor:
cursor.execute('SELECT ...')
result = cursor.fetchall()
5.3 参数化查询:防SQL注入的标准姿势
新手最容易犯的错误是用字符串拼接来构造SQL,比如:
python复制# 危险写法,千万别学
username = "zhangsan'; DROP TABLE users; --"
sql = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(sql)
这就是经典的SQL注入攻击。用户输入的内容被直接拼进SQL里,如果输入了恶意内容,就可能执行你没有预料的操作。正确做法是使用参数化查询:
python复制sql = "SELECT * FROM users WHERE username = %s AND points > %s"
cursor.execute(sql, (username, 100))
注意,%s是占位符,真正的值通过第二个参数传进去,pymysql会自动帮你做转义和类型处理。这个习惯从第一天学的时候就要养成,后面写Web应用、写接口,这就是保命技能。
5.4 写数据要commit,这是新手最容易漏的一步
刚才查询不需要提交,但如果你执行INSERT、UPDATE、DELETE,必须调用conn.commit(),改动才会真正写入数据库。如果不提交,数据看起来"好像没变",其实是被事务卡住了。
python复制cursor.execute("INSERT INTO users (username, email, points, register_time) VALUES (%s, %s, %s, %s)",
('wangwu', 'wangwu@example.com', 0, '2024-06-01 10:00:00'))
conn.commit()
如果你执行了写操作但没commit,程序退出后数据就会丢失,这也是一大经典翻车现场。反过来,如果SQL执行出错了,可以调用conn.rollback()回滚,把未提交的操作撤销掉。忘了commit,数据不生效;错了没rollback,脏数据残留,这两件事都要心里有数。
6. 实际项目中的连接与管理:把代码写得稳一点
6.1 封装一个简单的数据库工具类
项目里如果每个函数都写一遍pymysql.connect,代码会非常臃肿。实际开发中一般会封装一个数据库工具类或模块。我写一个简化版供参考:
python复制import pymysql
class DB:
def __init__(self, host, port, user, password, database):
self.config = {
'host': host,
'port': port,
'user': user,
'password': password,
'database': database,
'charset': 'utf8mb4',
'autocommit': False
}
def __enter__(self):
self.conn = pymysql.connect(**self.config)
self.cursor = self.conn.cursor()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.conn.rollback()
else:
self.conn.commit()
self.cursor.close()
self.conn.close()
def query(self, sql, args=None):
self.cursor.execute(sql, args)
return self.cursor.fetchall()
def execute(self, sql, args=None):
return self.cursor.execute(sql, args)
用起来是这样的:
python复制with DB('127.0.0.1', 3306, 'root', '密码', 'demo_db') as db:
rows = db.query("SELECT * FROM users WHERE points > %s", (100,))
for row in rows:
print(row)
这样封装的好处是:连接和关闭由__enter__/__exit__统一管理;事务的提交和回滚也自动处理——只要with块内的代码没抛异常,就提交;抛异常就回滚。项目代码会干净很多。
6.2 连接池:高并发场景下的必修课
再往后走,如果你要写Web后端,每个请求都新建一个数据库连接,性能会非常差。因为建立连接本身是有开销的,包括TCP握手、认证、资源分配。高并发时连接数还会把数据库压垮。解决方案就是连接池——预先创建一批连接放在池子里,用的时候借,用完还,避免频繁创建和销毁。
Python里常用的连接池工具有DBUtils,配合pymysql使用:
bash复制pip install DBUtils
python复制from dbutils.pooled_db import PooledDB
import pymysql
pool = PooledDB(
creator=pymysql,
maxconnections=20,
mincached=5,
maxcached=20,
blocking=True,
host='127.0.0.1',
port=3306,
user='root',
password='密码',
database='demo_db',
charset='utf8mb4'
)
# 使用
conn = pool.connection()
cursor = conn.cursor()
cursor.execute('SELECT ...')
...
cursor.close()
conn.close() # 不是真关闭,而是还回连接池
maxconnections=20表示最多20个连接,mincached=5表示池中至少保持5个空闲连接。blocking=True表示连接不够用时,请求排队等待。这个知识你现在不一定要马上用,但心里有个概念,等写到Web项目时会非常有帮助。
7. 从入门到进阶:数据库优化的第一课
7.1 慢查询该从哪些角度排查
当你写的程序跑得慢了,很多人第一反应是"肯定是Python代码效率低",但很多时候拖后腿的是数据库查询。排查的第一步是找到慢查询。MySQL提供了慢查询日志,可以这么开启:
sql复制SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 超过1秒的SQL会被记录
然后看日志文件,找到那些耗时高的SQL,再用EXPLAIN分析执行计划:
sql复制EXPLAIN SELECT * FROM orders WHERE user_id = 1;
EXPLAIN会告诉你这个查询有没有走索引、扫描了多少行、用了什么连接类型。你会看到type字段有ALL、index、range、ref等,ALL表示全表扫描,基本就是性能瓶颈的信号——这时候在上面这个例子里,你就应该给user_id建索引:
sql复制CREATE INDEX idx_user_id ON orders(user_id);
对很多新手项目来说,加一个合适的索引,查询速度往往能提升几十倍甚至上百倍,这是性价比最高的优化手段。
7.2 事务与ACID:面试常考,实战常用
简单提一下事务,因为MySQL的InnoDB默认支持事务。一个事务就是一组SQL操作,要么全部成功,要么全部失败回滚。
比如转账操作:从A账户扣100,给B账户加100。这两条SQL必须同时成功或同时失败,不能出现只扣钱不加钱的情况。在MySQL里,用BEGIN或START TRANSACTION开启事务,COMMIT提交,ROLLBACK回滚。
事务有四个核心特性,简称ACID:
- 原子性(Atomicity):事务里的操作要么全部完成,要么全部不完成。
- 一致性(Consistency):事务执行前后,数据的完整性约束不被破坏。
- 隔离性(Isolation):多个事务并发执行时,互不干扰。
- 持久性(Durability):事务提交后,对数据的修改是永久的。
这四个字面试经常考,但更重要的是理解"为什么需要事务"。等你以后写订单、写支付、写库存相关的代码,就会明白事务就是保命的底线。
7.3 一条数据在MySQL里是怎么存下来的
为了更直观地理解,我用流水账来描述一条INSERT语句的执行过程:
- 客户端把SQL发给MySQL服务器;
- 服务器解析SQL、检查语法、进行权限校验;
- 优化器决定执行方案(比如是否走索引);
- 执行器执行,先查缓冲池(Buffer Pool)里有没有对应页,没有就从磁盘加载到内存;
- 在内存中修改数据,写入
redo_log(重做日志); - 写入
binlog(二进制日志); - 提交事务,客户端收到成功响应。
这个流程里,redo_log保证崩溃后数据不丢,binlog用于主从复制和数据恢复。你现在不需要把这个流程背下来,但知道"数据库写入不是直接改磁盘文件"这一点,以后理解性能问题会更有方向感。
8. 常见问题排查与避坑
8.1 连接类问题
表格里列几个最常见的连接报错和解决方案,都是我实际踩过或见别人踩过的:
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Access denied for user 'root'@'localhost' |
密码错误或用户权限不对 | 确认密码;检查用户是否允许从当前主机登录 |
Can't connect to MySQL server on '127.0.0.1' (10061) |
服务没启动,或端口不对 | 启动MySQL服务;确认端口号是否映射正确 |
Authentication plugin 'caching_sha2_password' cannot be loaded |
pymysql版本太老,不支持MySQL 8.0默认认证 | pip install --upgrade pymysql |
Unknown database 'demo_db' |
数据库不存在 | 用SHOW DATABASES;查看已有库,确认库名 |
Table 'xxx' doesn't exist |
表名写错,或者没切换到正确的库 | 检查表名;执行USE 库名;切换 |
8.2 中文乱码问题
中文乱码是老生常谈,但真的还是会反复遇到。核心就三个环节保持统一:数据库字符集、连接字符集、Python内部编码。
数据库层面,建库时指定utf8mb4;连接层面,pymysql的charset参数设置为utf8mb4;Python代码里确保字符串本身没问题,一般用的是UTF-8。这三处都对了,基本不会乱码。
如果已经建好了库发现字符集不对,可以用下面SQL修改:
sql复制ALTER DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
8.3 一个让我印象深刻的报错:pymysql报Data too long for column
有次我在爬虫里抓了一段特别长的文本,直接往VARCHAR(50)里塞,就报了Data too long for column。这个问题的本质是列的长度不够。解决办法就是建表前先想清楚字段的最大长度,或者直接用TEXT类型:
sql复制CREATE TABLE articles (
id INT PRIMARY KEY AUTO_INCREMENT,
content TEXT NOT NULL
);
TEXT类型可以存64KB的文本,LONGTEXT可以存4GB。但要注意,TEXT类型不能像VARCHAR那样加默认值,索引时也只能指定前缀长度。所以能用VARCHAR解决的就用VARCHAR,确实需要大段文本才用TEXT。
另外一个很容易被忽略的细节:MySQL里
VARCHAR的单位是字符而不是字节,VARCHAR(50)最多存50个汉字,这对中文场景是友好的,不用自己换算字节数。
8.4 删除和更新前先备份
最后再啰嗦一句:任何DELETE和UPDATE操作,尤其是涉及生产数据的,写之前先备份。备份最笨但最稳妥的方式就是先查一遍看看影响范围:
sql复制-- 先看会删哪些
SELECT * FROM users WHERE created_at < '2020-01-01';
-- 确认无误后,再执行删除
DELETE FROM users WHERE created_at < '2020-01-01';
另外,MySQL有mysqldump工具,可以很方便地导出数据库备份:
bash复制mysqldump -u root -p demo_db > backup.sql
恢复的时候:
bash复制mysql -u root -p demo_db < backup.sql
养成"动数据之前先备份、先select"的习惯,能帮你躲过无数次灾难现场。
9. 从能跑到会用:我对数据库学习的几个建议
学数据库这事,最容易犯的毛病是"只看不练、一懂就停"。看视频看教程的时候觉得"哦原来如此",关上电脑第二天全忘了。我在这个阶段最大的体会是——SQL和Python不一样,它特别吃肌肉记忆。你不需要理解很深的原理,但必须得亲手敲过、亲手踩过那些报错,才能真正记住。
我建议你按这个顺序走一遍:装好MySQL → 在Workbench里把第4章的SQL从头到尾刷一遍 → 用pymysql在Python里连着做一遍CRUD → 试着把之前写的爬虫数据从JSON迁移到MySQL → 用Python写几个聚合查询做统计。这套流程走完,你就算真正告别了"文件存数据"的阶段。
另外一个很有用的练习方式,是去LeetCode或者牛客网上刷几道简单的SQL题。很多人觉得SQL不就算法题,但其实这些题会逼着你在各种条件组合、分组聚合、连表查询上动脑筋,比你自己瞎写几个示例要有效得多。
还有个小技巧分享给你:把官方文档当字典用,不要当小说读。MySQL的官方文档非常庞大,你不需要从头看到尾。遇到函数不会用、语法不确定,打开文档查对应章节就行。比如你想知道DATE_FORMAT的格式符是什么,直接搜文档里的"Date and Time Functions",几分钟就搞定了。
我当初学的时候就是看别人教程一步步照着敲,敲错了好多次,包括但不限于忘了写分号、单引号写成双引号、字段名拼错、忘记commit、pymysql版本太老连不上MySQL 8.0……这些错误看似低级,但每一个踩过之后,再遇到就会格外小心。数据库这东西,出错从来不是因为你笨,而是因为你接触得少。多来几次,就有手感了。
