web-dev-qa-db-ja.com

ビュー(およびベーステーブルだけでなく)を参照する外部キーを許可するDBMSはありますか?

Djangoモデリングの質問: Djangoで複数の多対多のリレーションを使用したデータベースモデリング に触発されました。db-designは次のようなものです。

CREATE TABLE Book
( BookID INT NOT NULL
, BookTitle VARCHAR(200) NOT NULL
, PRIMARY KEY (BookID)
) ;

CREATE TABLE Tag
( TagID INT NOT NULL
, TagName VARCHAR(50) NOT NULL
, PRIMARY KEY (TagID)
) ;

CREATE TABLE BookTag
( BookID INT NOT NULL
, TagID INT NOT NULL
, PRIMARY KEY (BookID, TagID)
, FOREIGN KEY (BookID)  REFERENCES Book (BookID)
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
) ;

CREATE TABLE Aspect
( AspectID INT NOT NULL
, AspectName VARCHAR(50) NOT NULL
, PRIMARY KEY (AspectID)
) ;

CREATE TABLE TagAspect
( TagID INT NOT NULL
, AspectID INT NOT NULL
, PRIMARY KEY (TagID, AspectID) 
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
, FOREIGN KEY (AspectID)  REFERENCES Aspect (AspectID)
) ;

db diagram

問題は、BookAspectRatingテーブルを定義し、参照整合性を適用する方法です。そのため、無効な(Book, Aspect)の組み合わせの評価を追加できません。

AFAIK、複雑なCHECK制約(またはASSERTIONS)はサブクエリとこれを解決する可能性のある複数のテーブルを含み、どのDBMSでも使用できません。

別のアイデアは、ビューを(擬似コード)使用することです:

CREATE VIEW BookAspect_view
  AS
SELECT DISTINCT
    bt.BookId
  , ta.AspectId
FROM 
    BookTag AS bt
  JOIN 
    Tag AS t  ON t.TagID = bt.TagID
  JOIN 
    TagAspect AS ta  ON ta.TagID = bt.TagID 
WITH PRIMARY KEY (BookId, AspectId) ;

上記のビューへの外部キーを持つテーブル:

CREATE TABLE BookAspectRating
( BookID INT NOT NULL
, AspectID INT NOT NULL
, PersonID INT NOT NULL
, Rating INT NOT NULL
, PRIMARY KEY (BookID, AspectID, PersonID)
, FOREIGN KEY (PersonID)   REFERENCES Person (PersonID)
, FOREIGN KEY (BookID, AspectID) 
    REFERENCES BookAspect_view (BookID, AspectID)
) ;

3つの質問:

  • (おそらく具体化された)VIEWPRIMARY KEYを許可するDBMSはありますか?

  • REFERENCES a VIEW(およびベースTABLEだけではない)FOREIGN KEYを許可するDBMSはありますか?

  • この整合性の問題は別の方法で解決できますか-利用可能なDBMS機能で?


明確化:

Django質問は私のものではありません!-問題に対する攻撃の可能性のある一般的な戦略ではなく、詳細な解決策ではありません。したがって、 "DBMS-Xでは、これはテーブルAのトリガーで実行できます"のような答えは完全に受け入れられます。

22
ypercubeᵀᴹ

このビジネスルールは、制約のみを使用してモデルに適用できます。次の表で問題を解決できます。あなたのビューの代わりにそれを使用してください:

    CREATE TABLE BookAspectCommonTagLink
    (  BookID INT NOT NULL
    , AspectID INT NOT NULL
    , TagID INT NOT NULL
--TagID is deliberately left out of PK
    , PRIMARY KEY (BookID, AspectID)
    , FOREIGN KEY (BookID, TagID) 
        REFERENCES BookTag (BookID, TagID)
    , FOREIGN KEY (AspectID, TagID) 
        REFERENCES AspectTag (AspectID, TagID)
    ) ;
9
A-K

多くの場合、複雑なビジネスルールはモデルだけでは適用できないことに気付くと思います。これは、少なくともSQL Serverでは、トリガー(できればトリガーではない)が目的に適していると考えるケースの1つです。

8
Aaron Bertrand

Oracleでは、宣言的な方法でこの種の制約を強制する1つの方法は、クエリがすべての無効な行(つまり、一致しないBookAspectRating行)を識別するコミットで高速に更新されるように設定されたマテリアライズドビューを作成することです。 BookAspect_view)。次に、マテリアライズドビューに行がある場合に違反する、そのマテリアライズドビューに簡単な制約を作成できます。これには、マテリアライズドビューで複製する必要があるデータの量を最小限に抑えるという利点があります。ただし、制約はトランザクションをコミットしている時点でのみ適用されるため、問題が発生する可能性があります。多くのアプリケーションは、コミット操作が失敗する可能性があることを想定して作成されていません。また、制約違反がいくらか難しい場合があるためです。特定の行または特定のテーブルに関連付ける。

8
Justin Cave

SIRA_PRISEはそれを許可します。

FKは「FK」ではなく「データベース制約」と呼ばれ、「ビュー」は実際にはビューとして定義する必要さえありませんが、ビューを定義する式を、データベース制約。

あなたの制約は次のようになります

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

これで完了です。

ただし、ほとんどのSQL DBMSでは、制約に対して分析作業を行い、どのように違反するかを判断し、必要なすべてのトリガーを実装する必要があります。

4
Erwin Smout

PostgreSQLでは、トリガーを使用せずに解決策を想像することはできませんが、確かにその方法で解決できます(ある種のマテリアライズドビューを維持するか、BookAspectRatingのbeforeトリガーを維持します)。ビューを参照する外部キーはありません(ERROR: referenced relation "v_munkalap" is not a table)、主キーはもちろんです。

3
dezso