Postgres:如何在部分索引上创建 FK 引用?

问题描述

我们想要维护两个表之间的参照完整性,但我们也想要软删除记录。用于创建表的 sql

CREATE TABLE table_a (
    id           SERIAL PRIMARY KEY,created_at   TIMESTAMP WITH TIME ZONE DEFAULT Now(),updated_at   TIMESTAMP WITH TIME ZONE DEFAULT Now(),deleted_at   TIMESTAMP WITH TIME ZONE,uuid         UUID DEFAULT uuid_generate_v4()
);

CREATE UNIQUE INDEX table_a_uuid ON table_a (uuid) WHERE deleted_at IS NULL;

CREATE TABLE association (
    id               SERIAL PRIMARY KEY,created_at       TIMESTAMP WITH TIME ZONE DEFAULT Now(),updated_at       TIMESTAMP WITH TIME ZONE DEFAULT Now(),deleted_at       TIMESTAMP WITH TIME ZONE,table_a_uuid     UUID REFERENCES table_a (uuid) NOT NULL
);

问题是,Postgres 不允许外键引用部分索引,其中 association.table_a_uuid 是。有什么办法可以在 Psql 中维护这些表之间的引用完整性,还是必须在应用程序层中进行?

编辑:要添加一些额外的上下文,说明为什么我们要使用 uuid 作为 FK 引用,table_a 上的软删除记录表示记录的旧“版本”,但所有“版本” " 具有相同的 uuid。引用 association 表应“指向”最新版本的 table_a 记录。

解决方法

有一种相当笨拙的方式来做你想做的事。这个想法是一个由两部分组成的索引,一个带有 uuid,第二个带有 id——但仅用于已删除的记录。对于未删除的,它有一个常数值,-1很方便。

他们可以构造一个外键引用:

CREATE TABLE table_a (
    id           SERIAL PRIMARY KEY,created_at   TIMESTAMP WITH TIME ZONE DEFAULT NOW(),updated_at   TIMESTAMP WITH TIME ZONE DEFAULT NOW(),deleted_at   TIMESTAMP WITH TIME ZONE,id_if_deleted  int generated always as (case when deleted_at is null then -1 else id end) stored,uuid         UUID DEFAULT gen_random_uuid()
);

CREATE UNIQUE INDEX table_a_uuid ON table_a (uuid,id_if_deleted) ;

CREATE TABLE association (
    id               SERIAL PRIMARY KEY,created_at       TIMESTAMP WITH TIME ZONE DEFAULT NOW(),updated_at       TIMESTAMP WITH TIME ZONE DEFAULT NOW(),deleted_at       TIMESTAMP WITH TIME ZONE,const_neg_one    int generated always as (-1) stored,table_a_uuid     UUID,foreign key (table_a_uuid,const_neg_one) REFERENCES table_a (uuid,id_if_deleted )
);