10

次のシナリオでテーブルを設計する最良の方法を見つけようとしています:

私のシステムにはいくつかの領域 (ドキュメント、プロジェクト、グループ、およびクライアント) があり、それぞれにログに記録されたコメントを含めることができます。

私の質問は、次のようなテーブルを 1 つ持つべきかということです。

CommentID
DocumentID
ProjectID
GroupID
ClientID
etc

ID の 1 つだけにデータがあり、残りは NULL になる場合、または別の CommentType テーブルを用意して、コメント テーブルを次のようにする必要があります。

CommentID
CommentTypeID
ResourceID (this being the id of the project/doc/client)
etc

私の考えでは、インデックス作成の観点からは、オプション 2 の方が効率的です。これは正しいです?

4

10 に答える 10

7

オプション2は、リレーショナルデータベースには適していません。これは多形関係と呼ばれ(@Daniel Vassalloが言及しているように)、関係の基本的な定義を破ります。

たとえば、2つの異なる行に1234のResourceIdがあるとします。これらは同じリソースを表していますか?CommentTypeIdがこれらの2つの行で同じであるかどうかによって異なります。これは、リレーションのの概念に違反します。詳細については、SQLおよびCJ日付による関係理論を参照してください。

壊れた設計であるというもう1つの手がかりは、ResourceIdの外部キー制約を宣言できないことです。これは、複数のテーブルのいずれかを指す可能性があるためです。トリガーなどを使用して参照整合性を適用しようとすると、新しいタイプのコメント可能なリソースを追加するたびにトリガーを書き直すことになります。

@mdmaが簡単に言及している(しかし無視している)解決策でこれを解決します:

CREATE TABLE Commentable (
  ResourceId INT NOT NULL IDENTITY,
  ResourceType INT NOT NULL,
  PRIMARY KEY (ResourceId, ResourceType)
);

CREATE TABLE Documents (
  ResourceId INT NOT NULL,
  ResourceType INT NOT NULL CHECK (ResourceType = 1),
  FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable
);

CREATE TABLE Projects (
  ResourceId INT NOT NULL,
  ResourceType INT NOT NULL CHECK (ResourceType = 2),
  FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable
);

現在、各リソースタイプには独自のテーブルがありますが、シリアル主キーはCommentableによって一意に割り当てられます。特定の主キー値は、1つのリソースタイプでのみ使用できます。

CREATE TABLE Comments (
  CommentId INT IDENTITY PRIMARY KEY,
  ResourceId INT NOT NULL,
  ResourceType INT NOT NULL,
  FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable
);

現在、コメントはコメント可能なリソースを参照しており、参照整合性が適用されています。特定のコメントは、1つのリソースタイプのみを参照できます。異常や競合するリソースIDの可能性はありません。

プレゼンテーション「SQLの実用的なオブジェクト指向モデル」と私の本「SQLアンチパターン」で、ポリモーフィックな関連付けについて詳しく説明します。

于 2010-06-16T17:05:20.067 に答える
6

データベースの正規化について読んでください。

あなたが説明する方法のヌルは、データベースが適切に設計されていないことを示す大きな兆候です。

テーブルに保持されているデータが完全に正規化されるように、すべてのテーブルを分割する必要があります。これにより、より多くの時間を確実に節約できます。習慣化することをお勧めします。

于 2010-06-16T16:24:57.353 に答える
3

外部キーの観点からは、1 つの列に複数の外部キー制約を設定できるため、最初の例の方が優れていますが、これらすべての参照にデータが存在する必要があります。また、ビジネス ルールが変更された場合にも柔軟に対応できます。

于 2010-06-16T16:24:35.330 に答える
3

@OMG Ponies の answerから続けると、2 番目の例で説明したものはPolymorphic Associationと呼ばれ、外部キーResourceIDが複数のテーブルの行を参照する場合があります。ただし、SQL データベースでは、外部キー制約は正確に 1 つのテーブルしか参照できません。データベースは、 の値に従って外部キーを適用できませんCommentTypeID

この問題に対処するための 1 つの解決策については、次の Stack Overflow の投稿をご覧ください。

于 2010-06-16T16:25:59.880 に答える
2

最初のアプローチは、かなり非正規化されているため、優れたものではありません。新しいエンティティ タイプを追加するたびに、テーブルを更新する必要があります。これをドキュメントの属性にする方がよいかもしれません。つまり、コメントをドキュメント テーブルにインラインで保存します。

参照整合性を使用するResourceIDアプローチでは、ドキュメント、プロジェクトなどのすべてのエンティティにResourceテーブルと外部キーが必要です (またはマッピング テーブルを使用します)。 ResourceID-trades (documentID、projectID など) は、適切なインデックス作成や外部キー制約に使用できないため、適切なソリューションではありません。

正規化するには、コメント テーブルをリソース タイプごとに 1 つのテーブルにする必要があります。

Comment
-------
CommentID
CommentText
...etc 

DocumentComment
---------------
DocumentID
CommentID

ProjectComment
--------------
ProjectID
CommentID

コメントが 1 つしか許可されていない場合は、エンティティ (DocumentID、ProjectID など) の外部キーに一意の制約を追加します。これにより、特定のアイテムに対して 1 つの行しか存在できないため、コメントは 1 つだけになります。また、CommentID で一意の制約を使用して、コメントが共有されないようにすることもできます。

編集: 興味深いことに、これは ResourceID の正規化された実装とほぼ同じです。テーブル名の「Comment」を「Resource」に置き換え、「CommentID」を「ResourceID」に変更すると、ResourceID を各リソースに関連付けるために必要な構造が得られます。 . その後、単一のテーブル「ResourceComment」を使用できます。

任意のタイプのリソース (監査の詳細、アクセス権など) に関連付けられている他のエンティティがある場合は、正規化されたコメントを追加できるため、リソース マッピング テーブルを使用する方法が適しています。およびその他のリソース関連エンティティ。

于 2010-06-16T16:32:35.610 に答える
1

私はこれらのソリューションのどちらにも行きません。要件の詳細によっては、スーパータイプのテーブルを使用できます。

CREATE TABLE Commentable_Items (
    commentable_item_id    INT    NOT NULL,
    CONSTRAINT PK_Commentable_Items PRIMARY KEY CLUSTERED (commentable_item_id))
GO
CREATE TABLE Projects (
    commentable_item_id    INT    NOT NULL,
    ... (other project columns)
    CONSTRAINT PK_Projects PRIMARY KEY CLUSTERED (commentable_item_id))
GO
CREATE TABLE Documents (
    commentable_item_id    INT    NOT NULL,
    ... (other document columns)
    CONSTRAINT PK_Documents PRIMARY KEY CLUSTERED (commentable_item_id))
GO

各項目に 1 つのコメントのみを含めることができ、コメントが共有されない場合 (つまり、コメントは 1 つのエンティティにのみ属することができる)、コメントを Commentable_Items テーブルに入れることができます。それ以外の場合は、そのテーブルのコメントを外部キーにリンクできます。

ただし、「コメントを付ける」だけではそのようにアイテムをまとめるのに十分ではないため、このアプローチはあなたの特定のケースではあまり好きではありません。

おそらく、個別のコメント テーブルを使用します (アイテムごとに複数のコメントを含めることができると仮定します。それ以外の場合は、それらをベース テーブルに入れるだけです)。複数のエンティティ タイプ間でコメントを共有できる場合 (つまり、ドキュメントとプロジェクトが同じコメントを共有できる場合)、中央のコメント テーブルと複数のエンティティとコメントの関係テーブルを用意します。

CREATE TABLE Comments (
    comment_id    INT            NOT NULL,
    comment_text  NVARCHAR(MAX)  NOT NULL,
    CONSTRAINT PK_Comments PRIMARY KEY CLUSTERED (comment_id))
GO
CREATE TABLE Document_Comments (
    document_id    INT    NOT NULL,
    comment_id     INT    NOT NULL,
    CONSTRAINT PK_Document_Comments PRIMARY KEY CLUSTERED (document_id, comment_id))
GO
CREATE TABLE Project_Comments (
    project_id     INT    NOT NULL,
    comment_id     INT    NOT NULL,
    CONSTRAINT PK_Project_Comments PRIMARY KEY CLUSTERED (project_id, comment_id))
GO

コメントを 1 つのドキュメントに限定したい場合 (たとえば)、そのリンク テーブル内の comment_id に一意のインデックスを追加 (または主キーを変更) できます。

特定の PK と FK に影響を与えるのは、これらの「小さな」決定のすべてです。私はこのアプローチが気に入っています。データベースでは、通常、「一般的な」テーブル/ソリューションを使用するよりも優れています。

于 2010-06-16T17:56:06.360 に答える
0

あなたが与える選択肢のうち、私は2番を選びます。

于 2010-06-16T16:24:28.870 に答える
0

質屋申し込み:

ローン、購入、在庫、および販売トランザクション用に個別のテーブルがあります。各テーブルの行は、次の方法でそれぞれの顧客の行に結合されます。

customer.pk [serial] = loan.fk [integer];
                     = purchase.fk [integer];
                     = inventory.fk [integer];
                     = sale.fk [integer]; 

4 つのテーブルを「トランザクション」と呼ばれる 1 つのテーブルに統合しました。

transaction.trx_type char(1) {L=ローン、P=購入、I=在庫、S=売却}

シナリオ:

顧客は最初に商品を質入れし、数回の利払いを行い、質屋に商品を販売することを決定します。質屋は商品を在庫に置き、最終的に別の顧客に販売します。

たとえば、次のような一般的なトランザクション テーブルを設計しました。

transaction.main_amount DECIMAL(7,2)

ローン取引ではポーンの金額を保持し、購入では購入価格を保持し、在庫と販売では販売価格を保持します。

これは明らかに非正規化された設計ですが、プログラミングが非常に簡単になり、パフォーマンスが向上しました。別のテーブルに変更する必要なく、1 つの画面内からあらゆるタイプのトランザクションを実行できるようになりました。

于 2010-06-16T23:12:55.797 に答える
0

オプション 2 は良い方法です。私が見ている問題は、そのテーブルにリソースキーを配置していることです。異なるリソースの各 ID が重複している可能性があります。リソースをコメントに結合すると、その特定のリソースに属さないコメントが表示される可能性が高くなります。これは、多対多の結合と見なされます。リソース テーブル、コメント テーブル、そしてリソース タイプとコメント テーブルを相互参照するテーブルを用意する方がよいと思います。

于 2010-06-16T16:34:34.897 に答える
0

コメントの内容に関係なく、すべてのコメントについて同じ種類のデータを保持している場合は、複数のコメント テーブルを作成することに反対します。コメントは「内容」とテキストだけかもしれませんが、現在他のデータがない場合は、コメントが入力された日付、コメントを作成した人のユーザー ID などを使用する可能性があります。テーブルごとにこれらすべての列定義を繰り返す必要があります。

前述のように、単一の参照フィールドを使用するということは、外部キー制約を設定できないことを意味します。これは残念ですが、何も壊れません。トリガーまたはコードで検証を行う必要があることを意味します。もっと深刻なことに、結合は難しくなります。「(documentid)を使用してコメント結合ドキュメントから」と言うだけです。type フィールドの値に基づく複雑な結合が必要です。

したがって、複数のポインター フィールドは見苦しいですが、それが正しい方法だと私は考える傾向があります。一部の db の人々は、テーブルに null フィールドがあってはならない、それを防ぐために常に別のテーブルに分割する必要があると言っていますが、このルールに従うことの実際の利点は見当たりません。

個人的には、賛否両論についてのさらなる議論を聞くことにオープンです.

于 2010-06-16T16:54:17.883 に答える