web-dev-qa-db-ja.com

複数のテーブルからのカスケード削除

PostgreSQL DBにrestaurantテーブルを含むかなり標準的なレストランモデルがあります。すべてのレストランには評価があります(これはaveragevote_countvote_sumで構成されています)。たとえば、picturesに対してこの評価スキーマを繰り返さないようにするために、ratingテーブルを個別に移動し、rating_idのみを保存しましたrestaurant内。

データベース全体で1つの評価が他の1つの行でのみ使用されることを知っています。 ratingまたはrestaurantの行を削除するときに、pictureに削除をカスケードする方法を教えてください。

私は周りを見回していますが、見つけることができるのはREFERENCESを設定することだけですが、そのためにはどのテーブルデータが削除されるかを知る必要があります。

私はtriggersが仕事をすることを知っています、私は単にもっとエレガントなものを望んでいました。

2
kelsohmm

いくつかの明確化:

  • REFERENCESは、 FOREIGN KEY constraint に使用されるキーワードWordです( カスケードDELETE またはUPDATE)。

  • データベースの設計に論理的な欠陥があるようです。 ratingは、メインテーブルrestaurantの詳細のようです。 1対1の関係があるので、メインテーブルに「評価」列を含めるだけで済みます。別のテーブルが必要な場合は、逆の方法ではなくratingテーブルにrestaurant_idを含めます。

  • 「評価」列average, vote_count and vote_sumは別のテーブルvoteを示し、これらの値は派生集計です。 MATERIALIZED VIEW がその典型的な解決策です。個別のratingテーブルとして、または各メインテーブルの列と組み合わせて...

クリーンな方法は、メインテーブルごとに個別のratingテーブルを用意することです。次に、ON DELETE CASCADEを使用して、それぞれにFK制約を設定できます。

Ifまだ現在のデザインが必要な場合、2つのアイデアがあります。

1. 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
);

UNIQUE制約は、1:1の要件を適用します。各マスターテーブルの各行は、ratingに最大でone行を持つことができます。また、非常に便利なインデックスを自動的に作成します。

あるいは、いくつかのメインテーブルがある場合は、各インデックスから無関係な行を除外するのではなく、部分的に一意のインデックスを作成することをお勧めします。

CREATE UNIQUE INDEX rating_restaurant_id ON rating (restaurant_id)
WHERE restaurant_id IS NOT NULL;
-- etc.

行ごとに1つの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 ...

いくつかのマスターテーブルがあると、大量の無駄なストレージのように見えるかもしれませんが、実際にはnotです。一連のNULL列はほとんどコストがかかりません。 NULLストレージは非常に安価です。

2. 継承

あなたの動機はto avoid repeating this rating schemaなので、 inheritance はあなたにとって良い解決策になるでしょう。 テーブル継承の制限 は、親テーブルの外部キーが継承されないことです-これはあなたの場合に有利になります:

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?
  • 基本スキーマを一度だけ定義し、それから継承する必要があります。

  • 単一のシーケンスが添付されたすべての評価に共通のPK列があります。

  • 各子テーブルは、マスターテーブルのIDをPKおよびFKとして追加するため、1対1の関係が適用され、列に重要なインデックスが自動的に提供されます。

  • マスターテーブルにクエリを実行して、すべての評価を一度に取得できます。各行がどの子テーブルから発生したかを知る必要がある場合:

    SELECT tableoid::regclass::text AS Origin, *
    FROM   rating;
    
  • マスターテーブルratingへの直接の挿入を禁止するルールまたはトリガーを追加することができます。

SQL Fiddle。

関連:

4