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


22

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図

問題は、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つの質問:

  • (おそらくマテリアライズド)VIEWを許可するDBMSはありPRIMARY KEYますか?

  • 許可DBMSがあるFOREIGN KEYこと(とベースだけではなく)?REFERENCESVIEWTABLE

  • そうでなければ、利用可能なDBMS機能を使用して、この整合性の問題を解決できますか?


明確化:

おそらく100%満足できるソリューションはないので、Djangoの質問は私のものでもありません!-詳細な解決策ではなく、問題に対する攻撃の一般的な戦略に興味があります。したがって、「DBMS-Xでは、これはテーブルAのトリガーを使用して実行できます」などの回答は完全に受け入れられます。


最初の2つの質問へのコメントとして投稿する-必ずしも知っているとは限りませんが、SQL Serverはビューの主キーまたは外部キーをサポートしていません。
アーロンバートランド

@アーロン:はい、ありがとう。OracleがビューでPKコストトレインをサポートしていることを読みました。しかし、この状況で機能するかどうかはわかりません。そして、2番目の質問(ビューへのFKについて)への答えは、おそらくOracleでは否定的です。
ypercubeᵀᴹ

しかし、私は他のソリューション(トリガー、チェックcostraintsまたは他のコンボ)があれば学ぶために興味を持っています
ypercubeᵀᴹは

回答:


9

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

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

いいね。私が考えることができる唯一の問題は、BookTagsとTagAspectsの挿入/削除で導入された複雑さです。たとえば、新しいBookTag(またはTagAspect)が削除されるたびに、このテーブルの対応する行を削除したりTagID、同じBookAspectの組み合わせに関連する別のタグに変更したりするために検索を行う必要があります。
ypercubeᵀᴹ

そして、これらの2つのテーブルに挿入するには、同様の検索を行う必要があります。しかし、複雑なルールは複雑な手順を必要とするため、これは本当に良いように見えます。
ypercubeᵀᴹ

@ypercubeタグを削除するとき、同じBookとAspectをリンクしている別のタグをチェックし、場合によっては切り替える必要があります。ただし、新しいタグを挿入する場合、評価を挿入する必要があるまでチェックを行う必要はありません。
AK

1
トラブルシューティング担当者とデータ入力担当者が同じ人である場合、またはエラーメッセージをエンドユーザーに公開する場合は、必ず確認してください。あなたは、あなたがすべてをしている一人の店について考えすぎています。
アーロンバートランド

4
@AaronBertrandあなたは私に大きな恩恵を与えました。「低メンテナンスデータベースの開発」というタイトルの記事を終えていますが、アプリサーバーはデータベースからの元のエラーメッセージをログに記録する必要があることを忘れていました。ちょうど追加しました。思い出してくれてありがとう;)
AK

8

多くの場合、モデルだけでは複雑なビジネスルールを実施できないことがわかると思います。これは、少なくともSQL Serverでは、トリガー(トリガーではなく)の方が目的に合っていると思います。


ちょっとアーロン、この場合、トリガーがエンティティといくつかの制約よりも良い選択である理由を説明できますか?
AK

2
@AlexKuznetsov確かに、複数の複数列の外部キーを使用してこれを実装する方法と、検証とエラー処理に対処するために必要となる可能性があるすべての追加ロジックを17時間費やしなかったからですか?
アーロンバートランド

2
トリガーの単純な実装がもたらす競合状態に注意してください。たとえば、最初のトランザクションがまだコミットされていないという理由だけで、あるトランザクションがタグから本を切断し、別のトランザクションが対応するアスペクトに接続しても問題ないと考える場合があります。@AlexKuznetsov answerによって導入された複雑さは、おそらく、トリガー(IMHO)の競合状態を防ぐために必要なロック「プロトコル」の複雑さと脆弱性よりも小さいでしょう。
ブランコディミトリエビッチ14年

8

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


4

SIRA_PRISEはそれを可能にします。

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

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

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

これで完了です。

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


知っている。これは、執筆時に重要だと思ったものを反映しています。
アーウィンスモート

3

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.