2016-04-24 15 views
2

種類が異なる任意の数の項目を作成するための次の表があります。2つの再帰的多対多関係間の制約

CREATE TABLE item_types (
    id SERIAL, 
    PRIMARY KEY (id) 
    -- Other columns omitted 
); 

CREATE TABLE items (
    id SERIAL, 
    itemtype integer NOT NULL, 
    PRIMARY KEY (id), 
    FOREIGN KEY (itemtype) REFERENCES item_types (id) 
    -- Other columns omitted 
); 

itemsテーブルはitem_relationshipと呼ばれる再帰的な多対多の関係を持っています。

CREATE TABLE item_relationships (
    itemid1 integer, 
    itemid2 integer, 
    PRIMARY KEY (itemid1, itemid2), 
    FOREIGN KEY (itemid1) REFERENCES items (id), 
    FOREIGN KEY (itemid2) REFERENCES items (id) 
); 

item_typesテーブルはitem_relationship_typesと呼ばれる再帰的な多対多の関係を持っています。今

CREATE TABLE item_relationship_types (
    type1 integer, 
    type2 integer, 
    PRIMARY KEY (type1, type2), 
    FOREIGN KEY (type1) REFERENCES item_types (id), 
    FOREIGN KEY (type2) REFERENCES item_types (id) 
); 

、私がやりたいことは何とか項目のitem_typesはどのitem_relationship_typeで発見されていない、あなたが誤って無効であるitem_relationshipを作成することはできません制約、すなわちを持つことです。私には2つの質問があります。

  1. このような制約は意味がありますか?私は間違った関係を挿入することはビジネスロジックで簡単に起こる可能性がある間違いだと思うので、DBの制約は重要だと感じています。

  2. 実際に制約を実装するにはどのような方法がありますか?

+0

注:ここでの再帰はありません。これは一種の*モデル - >インスタンス*継承パターンです。 – wildplasser

+1

"再帰的"とはどういう意味ですか?例えば ​​'item_relationship_types'テーブルが'(1,2)、(2,3)、(3,4) 'のレコードを持っている場合、タイプ1はタイプ2だけでなく、 3および4? – krokodilko

+0

@wildplasser説明をありがとう。 – ieyasu

答えて

1

一つは、代理の主キーを持つitem_relationship_typesテーブルを拡張することができ動作するように表示されます。

CREATE TABLE item_relationship_types (
    id integer SERIAL, 
    type1 integer, 
    type2 integer, 
    PRIMARY KEY (id), 
    UNIQUE (type1, type2), 
    FOREIGN KEY (type1) REFERENCES item_types (id), 
    FOREIGN KEY (type2) REFERENCES item_types (id) 
); 

とthエンitem_relationshipsテーブルにその代理キーを指す外部キーを追加します。

CREATE TABLE item_relationships (
    itemid1 integer, 
    itemid2 integer, 
    type_rel_id integer not null, 
    PRIMARY KEY (itemid1, itemid2), 
    FOREIGN KEY (itemid1) REFERENCES items (id), 
    FOREIGN KEY (itemid2) REFERENCES items (id), 
    FOREIGN KEY (type_rel_id) REFERENCES item_relationship_types (id) 
); 

あなたは、これらの二つの項目に関連していない種類のエントリを指すitem_relationshipsテーブルにtype_rel_id値を入力することを防止するトリガーを作成しても必要item_relationship_typesテーブル。

+0

私はトリガの変形を試みましたが、それは遅すぎることが判明しました。アイデアを広げ、サロゲートキーを作成するのではなく、item_relationship_typesからitem_relationshipsに直接 'type1'と' type2'を追加しました。 'item_relationships'では、' type2'と 'itemid2'に対して' FOREIGN KEY(type1、itemid1)REFERENCES items(itemtype、id) 'を追加しました。次に、 'FOREIGN KEY(type1、type2)REFERENCES item_relation_types(type1、type2)'を追加しました。これは制約を効率的に処理しますが、 'item1'と' type2'を 'item_relations'に追加する必要があると感じます。このソリューションの考えは? – ieyasu

1
  • これは完璧ではないが、可能性のあるアプローチの

CREATE FUNCTION item_check_types() RETURNS TRIGGER AS 
$func$ 
BEGIN 
IF EXISTS (
     SELECT 1 
     FROM item_relationship_types irt 
     JOIN items it1 ON it1.itemtype = irt.type1 
     JOIN items it2 ON it2.itemtype = irt.type2 
     WHERE (it1.id = NEW.itemid1 AND it2.id = NEW.itemid2) 
     -- OR (it1.id = NEW.itemid2 AND it2.id = NEW.itemid1) 
     ) THEN RETURN NEW; 
ELSE 
     RAISE EXCEPTION 'type lookup failure'; 
     RETURN NULL; 
END IF; 

END; 
$func$ LANGUAGE 'plpgsql' 
     ; 

CREATE CONSTRAINT TRIGGER item_check_types 
     AFTER UPDATE OR INSERT 
     -- BEFORE UPDATE OR INSERT 
     ON item_relationships 
     FOR EACH ROW 
     EXECUTE PROCEDURE item_check_types() 
     ; 

INSERT INTO item_types(id) 
SELECT generate_series(1,10); 

INSERT INTO item_relationship_types (type1, type2) VALUES 
(1,3), (2,4), (3,5), (4,6); 

INSERT INTO items(id, itemtype) 
SELECT gs, gs % 10 
FROM generate_series(101,109) gs; 

INSERT INTO item_relationships(itemid1, itemid2) 
    VALUES (101,103), (102,104); -- Okay 
INSERT INTO item_relationships(itemid1, itemid2) 
    VALUES (101,104), (102,103); -- should fail 
+0

'item_relationships'から対応する行を削除せずに' item_relationship_types'からレコードを削除するとどうなりますか?完全性は壊れるでしょう。 – krokodilko

+0

はい、その場合も処理する必要があります。 (残念ながら、カスケードはここでは不可能です) – wildplasser

+0

@ D4rt私は元のバージョンにロールバックしました。それは正しいと思われる。 – wildplasser