PostgreSQL循环外键无法创建,不删除外键的解决方法有哪些?
PostgreSQL循环外键问题解决方案
方案1:分阶段创建表和外键约束(无需修改表结构,最符合需求)
操作步骤如下:
- 第一步:创建两个基础表,仅定义主键和普通字段,暂不添加外键约束
-- 先创建users表 CREATE TABLE users ( user_id BIGINT NOT NULL PRIMARY KEY, -- 其他用户字段... last_seen_post BIGINT ); -- 再创建posts表 CREATE TABLE posts ( uid BIGINT NOT NULL PRIMARY KEY, sender_id BIGINT NOT NULL, -- 其他帖子字段... );
- 第二步:两个表都创建完成后,单独执行
ALTER TABLE添加外键约束
-- 给posts表的sender_id加外键,引用users.user_id ALTER TABLE posts ADD CONSTRAINT fk_posts_sender FOREIGN KEY (sender_id) REFERENCES users(user_id) ON DELETE CASCADE; -- 给users表的last_seen_post加外键,引用posts.uid ALTER TABLE users ADD CONSTRAINT fk_users_last_seen_post FOREIGN KEY (last_seen_post) REFERENCES posts(uid) ON DELETE SET NULL;
方案2:配合可延迟外键解决数据插入冲突
仅创建完外键还会遇到插入数据的逻辑冲突:插入用户时需要关联已存在的帖子,插入帖子时需要关联已存在的用户。可以通过将外键设为可延迟,在事务提交时再统一校验外键来解决:
-- 创建外键时添加DEFERRABLE INITIALLY DEFERRED参数 ALTER TABLE posts ADD CONSTRAINT fk_posts_sender FOREIGN KEY (sender_id) REFERENCES users(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; ALTER TABLE users ADD CONSTRAINT fk_users_last_seen_post FOREIGN KEY (last_seen_post) REFERENCES posts(uid) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
插入数据时放在同一个事务中执行即可:
BEGIN; -- 先插入用户,last_seen_post可以先填后面要插入的帖子uid INSERT INTO users (user_id, last_seen_post) VALUES (1, 1001); -- 再插入帖子,sender_id填刚才插入的用户ID INSERT INTO posts (uid, sender_id) VALUES (1001, 1); COMMIT;
注意事项
last_seen_post建议设置为允许NULL,新注册用户没有拉取过帖子时该字段可以为空,避免插入新用户时必须关联已存在帖子的问题- 外键的
ON DELETE SET NULL策略可以保证对应帖子被删除时,用户的last_seen_post自动置空,不会出现无效引用,也不会误删用户数据
内容的提问来源于stack exchange,提问作者Nocturn9X
相关产品推荐
相关产品推荐

