全部文章
工具笔记

让 AI 查 SQLite 外键:建表约束为何没拦住孤儿行

在连接初始化时验证约束是否真的执行。

资料核验2026.11.12 · SQLite · 本地数据库与小型服务

任务表声明了所属项目的外键,测试环境却能插入不存在的项目 ID。对于 SQLite,不能只靠建表语句判断当前连接是否启用了外键检查。

让助手从这一步开始

给助手表结构、连接初始化入口和一条能插入孤儿记录的复现。要求先查实际连接上的 PRAGMA 值,再判断是连接没开启检查、约束定义有误,还是旧数据早已存在。

流程示意:建立连接;开启并读回设置;拒绝无父记录

在每个连接建立后检查

显式执行 PRAGMA foreign_keys = ON,再读取设置确认值。不要等事务开始后才去开,因为事务中切换不会按预期生效。使用连接池时把初始化放在创建连接的路径上。

加一条直接走数据库的验证,尝试插入不存在的父记录,预期失败。迁移已有数据库后,用 foreign_key_check 查旧数据是否存在孤儿记录。

先处理旧数据再开放写入。只有模型层验证而数据库没开启约束,维护脚本仍可能绕过它。

把问题变成可重跑的样例

建项目和任务两张测试表。用连接 A 插入合法父子关系,用新连接 B 尝试把任务指向不存在的项目 999。记录每条连接的 PRAGMA 值与插入结果,再跑一次历史数据检查。

核对这几项输出

  • 每条新连接读回已启用。
  • 非法子记录直接写库失败。
  • 合法记录可插入。
  • 历史库用 foreign_key_check 单独报告已存在的问题。

怎么判断改动能不能收

把设置放在开始事务之前。使用连接池时,不能只在应用启动随便找一条连接执行。若旧数据违反约束,先决定修复或保留规则,再让助手准备迁移。

在测试连接中用同一插入语句比较初始化前后的结果。如果生产应用用连接池,至少开两条新的连接查设置。发现旧孤儿记录时把清理策略交给业务方确认,例如补父记录或标记数据异常。

前两条用于建立连接后设置并读回。最后一条可在测试库里检查既有数据违反约束的记录。

PRAGMA foreign_keys = ON;
PRAGMA foreign_keys;
PRAGMA foreign_key_check;

资料来源