如何解决两个相互引用的 postgresql 表
问题可能很基础,我对数据库没有任何经验。
我有一个带有一些表的 postgres 数据库。其中两个是 dates
和 accounts
。
date
表有一个 account_id
字段引用 id
表中的 account
表和一个 balance
字段表示帐户在那个日期。因此,许多 date
实体可能会引用一个 account
实体,多对一,可以。
但是 account
表也有一个 actual_date
字段,该字段必须引用 date
实体,以及该帐户的实际余额。一个 account
实体可能引用一个实际的 date
实体,但 date
实体可以有一个或零个 account
实体引用它。如果它确实有一个用 actual_date
引用它的帐户,它将始终是同一个帐户,date
本身用 account_id
引用。
这是什么关系?甚至有可能实施吗?如果是,我该怎么做?
我想出了这段代码,但我不知道它是否符合我的想法。
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,user_id INT REFERENCES users,actual_date_id DATE UNIQUE REFERENCES dates
);
CREATE TABLE dates (
id SERIAL PRIMARY KEY,account_id INT REFERENCES accounts,date DATE,balance INT,unconfirmed_balance INT
);
附言我使用 init.sql 创建表,但使用 sqlalchemy 使用它们,如果有人也能展示如何用它定义这样的模型,那就太好了。
解决方法
由于两个原因,SQL 脚本永远不会运行:
-
外键只能引用表的主键,不能引用表中的任意列。所以actual_date_id 应该是一个
integer
,以便能够引用dates
表的主键。 -
您不能引用尚未创建的表,因此帐户和日期之间的外键必须在创建两个表后创建。
使用循环外键通常更容易将它们中的至少一个定义为可延迟的,这样您就可以插入它们而无需例如中间 NULL 值。
所以有些东西(假设 users
已经存在)
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,user_id INT REFERENCES users,actual_date_id integer UNIQUE -- note the data type
);
CREATE TABLE dates (
id SERIAL PRIMARY KEY,account_id INT REFERENCES accounts,date DATE,balance INT,unconfirmed_balance INT
);
-- now we can add the foreign key from accounts to dates
alter table accounts
add foreign key (actual_date_id)
references dates (id)
deferrable initially deferred;
一开始最好避免循环引用。由于您想确保每个帐户只有一个“当前余额”,因此可以通过在日期表中添加一个标志并去掉帐户表中的 actual_date_id
来实现。
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,user_id INT REFERENCES users
);
CREATE TABLE dates (
id SERIAL PRIMARY KEY,is_current_balance boolean not null default false,unconfirmed_balance INT
);
-- this ensures that there is exactly one row with "is_current_balance = true"
-- for each account
create unique index only_one_current_balance
on dates (account_id)
where is_current_balance;
在将 dates
中的一行更改为“当前行”之前,您需要将现有行重置为 false
。
不相关,但是:
在现代 Postgres 版本中,recommended 使用 identity
列而不是 serial
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。