Skip to content

第 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受影响的行数

深入理解:为什么老手都"先查后改"? ​

真实业务中执行更新/删除的标准流程是:

  1. 先 SELECT:用同样的 where 条件查一遍,确认命中的就是你想动的那些行;
  2. 再 UPDATE/DELETE:确认无误后才执行写操作;
  3. 看 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 用来验证影响行数;
  • 软删除是物理删除的常见替代方案。