web-dev-qa-db-ja.com

複数のテーブルへの外部キー

データベースに関連するテーブルが3つあります。

CREATE TABLE dbo.Group
(
    ID int NOT NULL,
    Name varchar(50) NOT NULL
)  

CREATE TABLE dbo.User
(
    ID int NOT NULL,
    Name varchar(50) NOT NULL
)

CREATE TABLE dbo.Ticket
(
    ID int NOT NULL,
    Owner int NOT NULL,
    Subject varchar(50) NULL
)

ユーザーは複数のグループに属します。これは多対多の関係を介して行われますが、この場合は無関係です。チケットは、dbo.Ticket.Ownerフィールドを介して、グループまたはユーザーが所有できます。

MOST CORRECTチケットとオプションのユーザーまたはグループの間のこの関係を説明する方法は何でしょうか?

チケットテーブルに、所有しているタイプを示すフラグを追加する必要があると考えています。

109
Darthg8r

いくつかのオプションがあり、すべて「正確さ」と使いやすさが異なります。いつものように、適切な設計はニーズに依存します。

  • チケットにOwnedByUserIdとOwnedByGroupIdの2つの列を作成し、各テーブルにNULL入力可能な外部キーを設定するだけです。

  • Ticket:userとticket:groupの両方の関係を有効にするM:M参照テーブルを作成できます。おそらく将来的には、単一のチケットを複数のユーザーまたはグループが所有できるようにしたいと思うでしょうか?この設計では、チケットを1つのエンティティのみが所有する必要があります

  • すべてのユーザーに対してデフォルトグループを作成し、真のグループまたはユーザーのデフォルトグループのいずれかによってチケットを単純に所有させることができます。

  • または(私の選択)ユーザーとグループの両方のベースとして機能するエンティティをモデル化し、そのエンティティがチケットを所有します。

投稿したスキーマを使用した大まかな例を次に示します。

create table dbo.PartyType
(   
    PartyTypeId tinyint primary key,
    PartyTypeName varchar(10)
)

insert into dbo.PartyType
    values(1, 'User'), (2, 'Group');


create table dbo.Party
(
    PartyId int identity(1,1) primary key,
    PartyTypeId tinyint references dbo.PartyType(PartyTypeId),
    unique (PartyId, PartyTypeId)
)

CREATE TABLE dbo.[Group]
(
    ID int primary key,
    Name varchar(50) NOT NULL,
    PartyTypeId as cast(2 as tinyint) persisted,
    foreign key (ID, PartyTypeId) references Party(PartyId, PartyTypeID)
)  

CREATE TABLE dbo.[User]
(
    ID int primary key,
    Name varchar(50) NOT NULL,
    PartyTypeId as cast(1 as tinyint) persisted,
    foreign key (ID, PartyTypeId) references Party(PartyID, PartyTypeID)
)

CREATE TABLE dbo.Ticket
(
    ID int primary key,
    [Owner] int NOT NULL references dbo.Party(PartyId),
    [Subject] varchar(50) NULL
)
135
Nathan Skerl

@ Nathan Skerl のリストの最初のオプションは、私がかつて作業したプロジェクトで実装されたもので、3つのテーブル間で同様の関係が確立されていました。 (そのうちの1つは、他の2つを一度に1つずつ参照しました。)

そのため、参照元のテーブルには2つの外部キー列があり、1つの行で1つのテーブル(両方ではなく、どちらでもない)が参照されることを保証する制約もありました。

テーブルに適用した場合の外観は次のとおりです。

CREATE TABLE dbo.[Group]
(
    ID int NOT NULL CONSTRAINT PK_Group PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.[User]
(
    ID int NOT NULL CONSTRAINT PK_User PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.Ticket
(
    ID int NOT NULL CONSTRAINT PK_Ticket PRIMARY KEY,
    OwnerGroup int NULL
      CONSTRAINT FK_Ticket_Group FOREIGN KEY REFERENCES dbo.[Group] (ID),
    OwnerUser int NULL
      CONSTRAINT FK_Ticket_User  FOREIGN KEY REFERENCES dbo.[User]  (ID),
    Subject varchar(50) NULL,
    CONSTRAINT CK_Ticket_GroupUser CHECK (
      CASE WHEN OwnerGroup IS NULL THEN 0 ELSE 1 END +
      CASE WHEN OwnerUser  IS NULL THEN 0 ELSE 1 END = 1
    )
);

ご覧のとおり、TicketテーブルにはOwnerGroupOwnerUserの2つの列があり、どちらもNULL入力可能な外部キーです。 (他の2つのテーブルのそれぞれの列は、それに応じて主キーになります。)CK_Ticket_GroupUserチェック制約は、2つの外部キー列の一方のみが参照を含むことを保証します(もう一方はNULL )。

Ticket.IDの主キーは、この特定の実装には必要ありませんが、このようなテーブルにキーを入れても害はありません。)

27
Andriy M

さらに別のオプションは、Ticketに所有エンティティタイプ(UserまたはGroup)を指定する1つの列、参照されるUserまたはGroupを持つ2番目の列を持つことです。 idおよびNOTは外部キーを使用しますが、代わりにトリガーを使用して参照整合性を強制します。

Nathanの excellent model (上記)よりも優れている2つの利点:

  • より迅速な明快さとシンプルさ。
  • 書くためのより簡単なクエリ。
0
Jan Żankowski