SQLite 支持哪些约束类型?实际建表时怎么取舍?
SQLite 常用约束包括 NOT NULL、UNIQUE、PRIMARY KEY、FOREIGN KEY、CHECK 和 DEFAULT。它们的价值不是让建表语句更复杂,而是在数据写入时拦住明显错误。应用层校验会受版本、入口和脚本影响,数据库约束才是最后一道防线。
sqlPRAGMA foreign_keys = ON; CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE, age INTEGER CHECK(age >= 0), status TEXT NOT NULL DEFAULT 'active' ); CREATE TABLE orders ( id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, amount REAL NOT NULL CHECK(amount > 0), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE RESTRICT );
NOT NULL 表示字段必须有值,适合邮箱、订单金额、状态这类缺失后无法解释的数据。UNIQUE 保证业务唯一性,比如邮箱或外部订单号,但 SQLite 允许唯一列里出现多个 NULL,所以必填唯一字段要同时写 NOT NULL UNIQUE。INTEGER PRIMARY KEY 在 SQLite 里会关联 rowid,查询和插入都直接;复合主键适合关联表,但外键引用和查询条件会更啰嗦。
FOREIGN KEY 负责引用完整性,常见坑是 SQLite 需要每个连接执行 PRAGMA foreign_keys = ON;。CHECK 适合金额大于 0、状态取值范围这类稳定规则,不适合经常变化的产品策略。DEFAULT 只是缺省值,不是校验,它能减少插入语句重复,但不能掩盖真实缺失的数据。
追问
PRIMARY KEY 和 UNIQUE 有什么区别?
PRIMARY KEY 是一行记录的主身份,通常会被其他表引用。UNIQUE 只是说明某个业务字段不能重复,比如邮箱或 SKU。取舍上,主键最好稳定、短小、无业务含义;业务字段即使唯一,也可能因为手机号换绑、邮箱修改而变化。踩坑点是 UNIQUE 列可以有多个 NULL,这和很多人理解的“唯一”不一样。
外键约束是不是一定要开?
有明确父子关系时建议开启,尤其是订单、明细、用户这类不能孤立存在的数据。不开外键会让批量导入更省事,但孤儿记录会长期污染统计和删除逻辑。边界在于外键开关是连接级的,连接池、测试脚本和命令行工具都要设置。常见坑是开发连接开了外键,迁移脚本新建连接没开,结果约束没有真正生效。
CHECK 约束适合放哪些规则?
CHECK 适合放稳定、局部、只依赖本行字段的规则,比如价格不能小于 0。它不适合放需要查询其他表、调用外部服务或依赖复杂时间窗口的规则。取舍原则是:违反后一定代表数据无效的规则放数据库,只是运营策略的规则放应用层。踩坑点是后期补复杂约束可能需要重建表,迁移成本比一开始写清楚更高。
DEFAULT 能不能代替 NOT NULL?
不能。DEFAULT 只在插入时省略该列才生效,显式写入 NULL 时仍可能存进去。稳妥写法是 status TEXT NOT NULL DEFAULT 'active',既有默认值,也禁止空值。边界是默认值必须代表真实合理的业务状态,不要为了省事把未知数据伪装成正常数据。
约束会不会影响写入性能?
会有一点影响,尤其是 UNIQUE、外键和复杂 CHECK 都要额外判断。多数业务里,这点成本低于脏数据带来的修复成本。性能敏感场景可以给外键列补索引,批量导入时使用事务,并在导入后做一致性检查。不要为了插入快就删掉约束,除非你有同等可靠的校验和修复链路。