SQLite3如何实现值在多列间的全局唯一约束?
SQLite实现跨多列全局唯一约束的方案
SQLite 本身没有提供直接实现多列共享唯一校验规则的原生语法,可通过以下两种方式实现需求:
方案一:基于现有表结构使用触发器校验
不需要调整现有表结构,通过INSERT、UPDATE触发器做前置校验即可:
-- 插入链路前校验点是否已被占用 CREATE TRIGGER check_link_point_unique_before_insert BEFORE INSERT ON links FOR EACH ROW BEGIN SELECT RAISE(FAIL, '目标点已被其他链路占用') WHERE EXISTS ( SELECT 1 FROM links WHERE point_a = NEW.point_a OR point_b = NEW.point_a OR point_a = NEW.point_b OR point_b = NEW.point_b ); END; -- 更新链路前校验点是否已被占用(排除当前修改的链路自身) CREATE TRIGGER check_link_point_unique_before_update BEFORE UPDATE ON links FOR EACH ROW BEGIN SELECT RAISE(FAIL, '目标点已被其他链路占用') WHERE EXISTS ( SELECT 1 FROM links WHERE id != NEW.id AND (point_a = NEW.point_a OR point_b = NEW.point_a OR point_a = NEW.point_b OR point_b = NEW.point_b) ); END;
该方案的劣势是触发器逻辑隐式,后续维护表结构时容易遗漏校验规则,且插入/更新时需要全表扫描两个字段,性能略低。
方案二:优化表结构(更推荐)
调整表结构把点和链路的归属关系显性化,天然保证每个点只能属于一条链路,逻辑更清晰、性能更好:
CREATE TABLE links ( id INTEGER PRIMARY KEY AUTOINCREMENT -- 可按需添加链路的属性,比如创建时间、链路类型、带宽等 ); CREATE TABLE points ( id INTEGER PRIMARY KEY AUTOINCREMENT, x INTEGER NOT NULL, y INTEGER NOT NULL, link_id INTEGER REFERENCES links(id), -- 保证每个点最多关联一条链路,NULL表示未关联任何链路 UNIQUE(id, link_id) ); -- 补充约束:单条链路最多关联2个点,避免异常数据 CREATE TRIGGER check_link_point_count BEFORE INSERT ON points FOR EACH ROW WHEN NEW.link_id IS NOT NULL BEGIN SELECT RAISE(FAIL, '单条链路最多关联2个点') WHERE (SELECT COUNT(*) FROM points WHERE link_id = NEW.link_id) >= 2; END;
该方案的优势:
- 约束直观,查询点是否被占用直接读取points表对应行的link_id字段即可
- 不需要维护复杂的跨列校验逻辑,出错概率低
- 关联查询链路对应的点位性能和原有方案一致,插入/更新校验的性能更高
内容的提问来源于stack exchange,提问作者Hubro
相关产品推荐
相关产品推荐

