从多个表中级联删除

kel*_*hmm 3 postgresql database-design delete constraint cascade

我有一个相当标准的餐厅模型,restaurant在 PostgreSQL 数据库中有一张桌子。所有餐厅都有评分(由averagevote_count和组成vote_sum),但为了避免重复此评分模式,例如pictures我将它们移到单独的rating表中并仅存储rating_idin restaurant

我知道 1 个评级只会被整个数据库中的 1 个其他行使用。ratingrestaurant或 中删除行时如何级联删除picture

我一直在环顾四周,但我能找到的只是设置了 a REFERENCES,但为此我需要知道将删除哪些表数据。

我知道触发器可以完成这项工作,我只是希望有更优雅的东西。

Erw*_*ter 5

一些澄清:

  • REFERENCES是用于FOREIGN KEY约束的关键字(允许级联DELETEUPDATE)。

  • 您的数据库设计似乎存在逻辑缺陷。rating似乎是主表的细节restaurant。由于您有 1:1 的关系,您可以只在主表中包含“评级”列。如果您需要一个单独的表格,您可以restaurant_idrating表格中包含 a而不是相反。

  • 您的“评级”列average, vote_count and vote_sum表示另一个表vote,这些值是派生的聚合。AMATERIALIZED VIEW将是典型的解决方案。作为单独的rating表或与每个主表中的列组合...

干净的方法是rating为每个主表设置一个单独的表。然后你可以对每个都有一个 FK 约束ON DELETE CASCADE

如果您仍然需要当前的设计,我有两个想法:

1. 一张rating表中的多个 FK 列

将 FK 约束反向指向正确的方向后,该表可能如下所示:

CREATE TABLE rating (
  rating_id  serial PRIMARY KEY
, vote_count int
, vote_sum   int
, average    float8
, restaurant_id int UNIQUE REFERENCES restaurant(restaurant_id)
                    ON UPDATE CASCADE ON DELETE CASCADE
, picture_id int UNIQUE REFERENCES picture(picture_id)
                 ON UPDATE CASCADE ON DELETE CASCADE
-- more references to other tables
);
Run Code Online (Sandbox Code Playgroud)

UNIQUE约束可以加强你的1:1的要求。在每个主表中的每一行最多只能有一个rating。还自动创建非常有用的索引。

或者,如果您有多个主表,您可能希望创建部分唯一索引,而不是从每个索引中排除不相关的行:

CREATE UNIQUE INDEX rating_restaurant_id ON rating (restaurant_id)
WHERE restaurant_id IS NOT NULL;
-- etc.
Run Code Online (Sandbox Code Playgroud)

要强制每行只有一个 FK 列是NOT NULL

ALTER TABLE rating ADD CONSTRAINT exactly_one_fk CHECK (
      (restaurant_id IS NOT NULL)::int
    + (picture_id    IS NOT NULL)::int = 1);  -- add more ...
Run Code Online (Sandbox Code Playgroud)

使用几个主表,看起来可能会浪费很多存储空间,但实际上并非如此。一堆 NULL 列几乎不需要任何费用。NULL 存储非常便宜:

2.继承

由于您的动机是to avoid repeating this rating schema继承对您来说可能是一个很好的解决方案。表继承的一个限制是父表的外键不是继承的——这对你的情况来说是一个优势:

CREATE TABLE rating (
  rating_id  serial PRIMARY KEY
, vote_count int
, vote_sum   int
, average    float8
);

CREATE TABLE restaurant_rating (
   restaurant_id int PRIMARY KEY REFERENCES restaurant(restaurant_id)
                                 ON UPDATE CASCADE ON DELETE CASCADE
) INHERITS (rating);

CREATE TABLE picture_rating (
   picture_id int PRIMARY KEY REFERENCES picture (picture_id)
                              ON UPDATE CASCADE ON DELETE CASCADE
) INHERITS (rating);

-- more?
Run Code Online (Sandbox Code Playgroud)
  • 现在您只需定义一次基本架构并从中继承。

  • 您仍然有一个用于所有评级的公共 PK 列,并带有单个附加序列。

  • 每个子表都将主表的 ID 添加为 PK 和 FK,从而加强您的 1:1 关系并自动提供列上的重要索引。

  • 您可以查询主表以一次获取所有评级。如果您需要知道每一行来自哪个子表:

    SELECT tableoid::regclass::text AS origin, *
    FROM   rating;
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可能希望添加规则或触发器以禁止rating直接插入主表。

SQL小提琴。

有关的: