第 5 章 记录的更新与删除
从一个生活场景说起
更新和删除就像批改作业:
- 更新 = 用红笔改错:先找到要改的那本作业(
where),再动笔改(values); - 删除 = 把某些作业从作业堆里抽走扔掉:先找到要扔的(
where),再扔。
这里有个攸关性命的细节:如果你不说"改哪本",红笔会落到全班每一本作业上——update/delete 不写 where,作用对象就是整张表。历史上无数生产事故都源于一条漏了 WHERE 的 SQL。
核心概念
更新用 update(),删除用 delete(),和查询一样:构建语句 → execute() 执行 → commit() 确认(第 3 章的事务规则对写操作一视同仁)。
两个语句的结构高度对称:
python
update(表).where(改哪些行).values(改成什么)
delete(表).where(删哪些行)示例代码(逐行讲解)
python
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, update, delete
engine = create_engine(
"mysql+pymysql://root:123456@localhost:3306/demo_db?charset=utf8mb4",
echo=True,
)
metadata = MetaData()
user_table = Table(
"user", metadata,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("name", String(50), nullable=False),
Column("age", Integer),
)
with engine.begin() as conn: # begin 模式:正常退出自动 commit,出错自动 rollback
# ① 更新:把"张三"的年龄改为 21
stmt = (
update(user_table)
.where(user_table.c.name == "张三") # 先锁定目标行
.values(age=21) # 再指定新值
)
result = conn.execute(stmt)
print("更新了", result.rowcount, "行") # 看看改到了几行
# ② 删除:删除年龄小于 18 的记录
stmt = delete(user_table).where(user_table.c.age < 18)
result = conn.execute(stmt)
print("删除了", result.rowcount, "行")result.rowcount 是这里的好帮手:它告诉你这条语句实际影响了多少行。如果预期改 1 行、实际 rowcount 是 0 或 5,立刻就能发现条件写错了。
关键知识点
| 函数 | 作用 |
|---|---|
update(table).where(...).values(...) | 按条件更新 |
delete(table).where(...) | 按条件删除 |
result.rowcount | 受影响的行数 |
深入理解:为什么老手都"先查后改"?
真实业务中执行更新/删除的标准流程是:
- 先 SELECT:用同样的
where条件查一遍,确认命中的就是你想动的那些行; - 再 UPDATE/DELETE:确认无误后才执行写操作;
- 看 rowcount:验证影响行数符合预期。
口诀:"先瞄准,后开枪;开完枪,验弹孔。"
进阶内容
1. 软删除(Soft Delete):给数据"回收站"。
直接 DELETE 是物理删除,数据灰飞烟灭、无法恢复。很多业务系统改用软删除:
python
# 表里加一个标记列:Column("is_deleted", Boolean, default=False)
# "删除"用户其实是更新标记
update(user_table).where(user_table.c.id == 1).values(is_deleted=True)
# 查询时过滤掉已删除的
select(user_table).where(user_table.c.is_deleted == False)好处:误删可恢复、保留历史数据用于审计;代价:所有查询都要记得加过滤条件。
2. MySQL 方言的限量删除。 防止一条语句误删太多:
python
delete(user_table).where(user_table.c.age < 18).limit(100) # 最多删 100 行注意 .limit() 用在 delete 上是 MySQL 方言特性,不是所有数据库都支持。
3. 批量修改同一列为相对值。 比如给所有人年龄加 1:
python
update(user_table).values(age=user_table.c.age + 1)等号右边可以直接引用列本身,SQLAlchemy 会翻译成 SET age = age + 1。
注意事项
update/delete不带where会作用于全表,这是极其危险的操作,务必确认条件。- 真实业务中建议先
select确认要操作的记录,再执行更新/删除。 - 删除数据前考虑是否需要"软删除"(加一个
is_deleted字段标记,而不是物理删除)。 - MySQL 中可利用
LIMIT限制删除行数,防止误操作影响过大:delete(user_table).where(...).limit(100)(仅 MySQL 方言支持)。 - 生产环境执行大批量删除前,先评估对主从复制和锁的影响;分批删除比一次性删除更安全。
本章小结
- 更新/删除的结构:
update().where().values()、delete().where(); where是生命线,不写 = 动全表;rowcount用来验证影响行数;- 软删除是物理删除的常见替代方案。