web-dev-qa-db-ja.com

追加の制約がある外部キー?

Item(id, name, cost)Orders(id, bill_id, item_id, units)というテーブルがあり、注文を追跡するために作成されます。同じbill_idは、1つの注文に属することを意味します。

Orderテーブルにitem_idとして追加する必要がある場合、その時点で)Itemは(その時点で) "利用可能"であるというDBに追加の制約を課す方法?アイテムこのシナリオでは、「利用可能」として手動で決定され、データベース内の他のフィールドから派生することはできません。

1つのスキーマ設計(私が好む)は、「利用可能」および「利用不可」フィールドを持つType列を追加することです。しかし、どのようにして外部キー制約を確認できますかitem_idは主キーだけではないはずですItemテーブルでは、そのTypeも "Available"にする必要がありますか?

これ スタックオーバーフローの回答チェック制約を使用)は近いようですが、それが唯一の方法ですか?これはRDBMSの取るに足らないことだと思いますか、またはこれは正規化されたデータではありませんか?

他のスキーマ設計(私は好きではありません)は、「メニュー」と呼ばれるテーブルを作成することです。これには、のみアイテム使用可能)を含めることができます。問題は、このテーブルが本質的に非常に動的になり、アイテムの可用性に応じて変化し続け、状態に応じてアイテムからサブセットテーブルを作成しているだけですが、これはいいアイデアではないようです。

プログラムでこれを行うのは簡単です。ただし、RDBMSでこれをどのように実現しますか?私はデータベースがこれを処理するのに十分インテリジェントであるという考えが好きです。

6
Nishant

元の答えは間違っています!修正されたバージョンについては、編集1を参照してください。


元の答え

驚くべき解決策は、ビューの列または継承されたテーブルへの外部キーを必要としますが、残念ながらPostgreSQL(タグがあるため、RDBMSだと思います)には(まだ)ありません。

データを整理する方法の簡単な変更で十分だと思います。[ItemsAvailableQuantityのようなテーブルを作成し、Itemを注文の参照となる可用性と接続します。アイテムが利用できなくなった場合、そのアイテムからDELETEを取得します。

CREATE TABLE Item (
    id   serial  NOT NULL
  , name text    NOT NULL
  , cost numeric

  , PRIMARY KEY (id)
  , CONSTRAINT positive_cost
        CHECK (cost > 0)
    );

CREATE TABLE ItemAvailableQuantity (
    id       serial  NOT NULL
  , item_id  integer NOT NULL
  , quantity integer NOT NULL

  , PRIMARY KEY (id)
  , FOREIGN KEY (item_id)
        REFERENCES Item (id)
        ON UPDATE CASCADE
        ON DELETE CASCADE
  , CONSTRAINT postive_quantity -- This constraint is the same as
        CHECK (quantity > 0)    -- checking something like `available = TRUE`.
    );

CREATE TABLE ItemOrder (       -- Changed the name from `Order` because
    id      serial  NOT NULL   -- PostgreSQL refuses that name, somehow
  , bill_id integer NOT NULL
  , item_id integer NOT NULL
  , units   integer NOT NULL

  , PRIMARY KEY (id)
  , FOREIGN KEY (item_id)
        REFERENCES ItemAvailableQuantity (id)
        ON UPDATE CASCADE
        ON DELETE CASCADE
-- Uncomment when `Bill` table is ready
--  , FOREIGN KEY (bill_id)
--        REFERENCES Bill (id)
--        ON UPDATE CASCADE
--        ON DELETE CASCADE
  , CONSTRAINT positive_units
        CHECK (units > 0)
    );

注意!制約positive_unitsは、ソフトウェアが単位を減らして0に達すると問題を引き起こす可能性があります。CHECK >= 0必要に応じて、または各DELETEまたはINSERTnitsが0(またはそれ以下)に達したときに自動的にUPDATE- s行をトリガーするトリガーを追加します。これは、テーブルItemAvailableQuantityを保持して、実際に利用可能なアイテムのみを保持します。これは、テーブルItemOrderから参照するために必要なものです。

これで問題が解決するはずです。それはあなたの質問に対する正確な答えではありません。これには、トリガーまたはCHECKが提供したリンクのように関数を呼び出すことが含まれます。

アイテムの数量を簡単に確認するには、ItemAvailableQuantityItemを結合するビューを作成するだけです。本当に必要な場合は、INSERT- able トリガー付き(黄色のボックスの警告を参照) にしてください。


編集1

実際にはOrder(別名ItemOrder)はItemAvailableQuantityではなくItemを参照して、アイテムはコメントに記載されているように、現在利用できません。

これは、テーブル全体ItemAvailableQuantityを削除し、列available_quantity on Itemのみを追加する必要があることを示唆しています。

CREATE TABLE Item (
    id                 serial NOT NULL
  , name               text   NOT NULL
  , cost               numeric
  , available_quantity integer NOT NULL

  , PRIMARY KEY (id)
  , CONSTRAINT positive_cost
        CHECK (cost > 0)
  , CONSTRAINT non_negative_quantity
        CHECK (quantity >= 0)
    );

次に、利用可能なアイテムのみを注文に確実に挿入するために、実行できます

INSERT INTO ItemOrder (bill_id, item_id, units) VALUES
    (SELECT id FROM Bill WHERE condition = something -- customize at will
    , SELECT id FROM Item WHERE available_quantity >= wanted_quantity
        AND other_condition = something
    , wanted_quantity)
    ;

ここで、wanted_quantityは、ソフトウェアからクエリに渡されるパラメーターです。

それでも問題は解決しますが、質問に対する直接の回答ではありません。

3
Matjaž