PostgreSQL到多个表的循环外键

问题描述

我有3张桌子。

聊天室

create table chat_room
(
    id uuid default uuid_generate_v4() not null
        constraint chat_room_pk
            primary key
        constraint fk__chat_room__group_chat_room
            references group_chat_room
                on update cascade on delete cascade
        constraint fk__chat_room__private_chat_room
            references private_chat_room
                on update cascade on delete cascade,name varchar(255) not null,description varchar(255),profile_pic varchar(128),created_at timestamp with time zone default Now() not null,updated_at timestamp with time zone
);

group_chat_room

create table group_chat_room
(
    id uuid not null
        constraint group_chat_room_pk
            primary key
        constraint fk__group_chat_room___chat_room
            references chat_room
                on update cascade on delete cascade,pus_code char(7) not null
        constraint fk__group_chat_room__puskesmas
            references puskesmas
                on update cascade on delete cascade
);

create unique index group_chat_room_pus_code_uindex
    on group_chat_room (pus_code);

私人聊天室

create table private_chat_room
(
    id uuid not null
        constraint private_chat_room_pk
            primary key
        constraint fk__private_chat_room__chat_room
            references chat_room
                on update cascade on delete cascade
);

如您所见,chat_room具有引用group_chat_roomprivate_chat_room的外键约束。此外,group_chat_roomprivate_chat_room都具有引用chat_room的FK约束。

当我想INSERT连续group_chat_room行时,我会使用此

with chat_room as (
        insert into chat_room (id,name) values ('Some ID','Some Name')
    )
    insert into group_chat_room(id,pus_code) values ('Some ID','Some Code');

但是由于这些限制,这会产生错误

[23503] ERROR: insert or update on table "chat_room" violates foreign key constraint "fk__chat_room__private_chat_room" Detail: Key (id)=(cef8c655-d46a-4f63-bdc8-77113b1b74b4) is not present in table "private_chat_room".

如何仅插入group_chat_room而不必插入private_chat_room

解决方法

这里的主要问题是创建多个必需的双向外键。您最终可能可以解决它。但这会使数据模型更加复杂,而代码则更加复杂。这是完全没有必要。只需一张桌子就可以完成所有工作。如果分别需要group_chat_room和private_chat_room,则分别为它们创建一个视图。此外,作为简单视图,它们是完全可更新的。
您可以通过将“ pus_code”列移动到chat_room并添加2个布尔值来指示这是私人房间还是团体房间,或者同时使用这两者来完成此操作。是的,听起来很奇怪,您可以获得一个private_group_chat_room。 (注意:您的设计中没有任何东西可以阻止它,而得到的错误是因为它是必需的)。如果您确实希望这样做,则创建一个检查约束,要求至少一个布尔列为false。

create table chat_room
(
    id  integer generated always as identity
        constraint chat_room_pk
            primary key,name varchar(255) not null,description varchar(255),profile_pic varchar(128),is_private boolean not null default false,is_group boolean not null default false,pus_code varchar(7) 
        constraint fk__group_chat_room__puskesmas
            references puskesmas
                on update cascade on delete cascade,created_at timestamp with time zone default now() not null,updated_at timestamp with time zone,constraint not_group_of_pus_code_check
               check (   (not is_group and pus_code is null)
                      or (is_group and pus_code is not null)
                     ) 
);

-- create unique partial index
create unique index group_chat_room_pus_code_uindex on chat_room(pus_code)
    where is_group;

-- group_chat_room
create view group_chat_room 
       ( id,name,description,profile_pic,is_private,pus_code,created_at,updated_at
       ) as 
  select id,updated_at 
   from chat_room
  where is_group;

-- private_chat_room
create view private_chat_room                    
       ( id,is_group,updated_at                             
       ) as                                     
  select id,updated_at       
   from chat_room
  where is_private;

有关完整的示例和一些测试,请参见fiddle。注意:小提琴与generate_uuid_v4()存在问题(不存在),因此对于演示我更改为身份。在运行环境中会很好。