两个相互引用的 postgresql 表

Two postgresql tables referencing each other

问题可能很基础,我对数据库没有任何经验。

我有一个带有一些 table 的 postgres 数据库。其中两个是 datesaccounts.

date table 有一个 account_id 字段引用 id table 在 account table 和一个balance 表示该帐户在该日期的余额的字段。因此,许多 date 个实体可以引用一个 account 个实体,多对一,好吧。

但是 account table 也有一个 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
);

P.S。我使用 init.sql 创建 tables,但使用 sqlalchemy 与它们一起工作,如果有人也可以展示如何用它定义这样的模型,那就太好了。

如所写,SQL 脚本永远不会工作,原因有二:

  1. 外键只能引用table的主键,不能引用其中的任意列。所以 actual_date_id 应该是一个 integer 以便能够引用 dates table.

    的主键
  2. 您不能引用尚未创建的table,因此帐户和日期之间的外键必须在两个table都创建后创建.

使用循环外键,通常更容易将其中至少一个定义为可延迟的,这样您就可以插入它们而无需例如中间 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;

一开始就避免循环引用可能会更好。由于您想确保每个帐户只存在一个“当前余额”,这可以通过在日期 table 中添加一个标志并在帐户 [=51] 中删除 actual_date_id 来实现=].

CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    user_id INT REFERENCES users
);

CREATE TABLE dates (
    id SERIAL PRIMARY KEY,
    account_id INT REFERENCES accounts,
    is_current_balance boolean not null default false,
    date DATE,
    balance INT,
    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