外部キーフィールドの2つのテーブル間で循環参照があっても問題ありませんか?
そうでない場合、これらの状況をどのように回避できますか?
もしそうなら、どのようにデータを挿入できますか?
以下は、(私の意見では)循環参照が受け入れられる例です。
CREATE TABLE Account
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50)
)
CREATE TABLE Contact
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50),
AccountID INT FOREIGN KEY REFERENCES Account(ID)
)
ALTER TABLE Account ADD PrimaryContactID INT FOREIGN KEY REFERENCES Contact(ID)
外部キーにnull許容フィールドを使用しているため、実際には、想定したとおりに正しく機能するシステムを構築できます。 Accountsテーブルに行を挿入するには、PrimaryContactIDがnullのアカウントへの挿入を許可しない限り、Contactsテーブルに行が存在する必要があります。 Account行がまだ存在しない状態で連絡先行を作成するには、ContactsテーブルのAccountID列をnullにできるようにする必要があります。これにより、アカウントに連絡先を設定できなくなり、連絡先にアカウントを設定できなくなります。おそらくこれは望ましいことでしょう。
そうは言っても、私の個人的な好みは、次の設定にすることです。
CREATE TABLE dbo.Accounts
(
AccountID INT NOT NULL
CONSTRAINT PK_Accounts
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, AccountName VARCHAR(255)
);
CREATE TABLE dbo.Contacts
(
ContactID INT NOT NULL
CONSTRAINT PK_Contacts
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, ContactName VARCHAR(255)
);
CREATE TABLE dbo.AccountsContactsXRef
(
AccountsContactsXRefID INT NOT NULL
CONSTRAINT PK_AccountsContactsXRef
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, AccountID INT NOT NULL
CONSTRAINT FK_AccountsContactsXRef_AccountID
FOREIGN KEY REFERENCES dbo.Accounts(AccountID)
, ContactID INT NOT NULL
CONSTRAINT FK_AccountsContactsXRef_ContactID
FOREIGN KEY REFERENCES dbo.Contacts(ContactID)
, IsPrimary BIT NOT NULL
CONSTRAINT DF_AccountsContactsXRef
DEFAULT ((0))
, CONSTRAINT UQ_AccountsContactsXRef_AccountIDContactID
UNIQUE (AccountID, ContactID)
);
CREATE UNIQUE INDEX IX_AccountsContactsXRef_Primary
ON dbo.AccountsContactsXRef(AccountID, IsPrimary)
WHERE IsPrimary = 1;
これにより、次のことが可能になります。
IX_AccountsContactsXRef_Primary
インデックスを介して、メンテナンス性の高い主要連絡先のリストを提供します。このインデックスにはフィルターが含まれているため、フィルターをサポートするプラットフォームでのみ機能します。このインデックスはUNIQUE
オプションで指定されるため、アカウントごとに1つの主要連絡先しか存在できません。たとえば、すべての連絡先のリストを表示し、列が「メイン」のステータスであることを示し、アカウントごとにリストの一番上にメインの連絡先を表示する場合は、次のようにします。
SELECT A.AccountName
, C.ContactName
, XR.IsPrimary
FROM dbo.Accounts A
INNER JOIN dbo.AccountsContactsXRef XR ON A.AccountID = XR.AccountID
INNER JOIN dbo.Contacts C ON XR.ContactID = C.ContactID
ORDER BY A.AccountName
, XR.IsPrimary DESC
, C.ContactName;
フィルターされたインデックスにより、アカウントごとに複数の主要連絡先が挿入されるのを防ぎ、同時に主要連絡先のリストを返す迅速な方法を提供します。連絡先がアカウントに関連付けられなくなった後でも、アカウントごとの連絡先の履歴を維持するために、一意でないフィルター処理されたインデックスを持つ別の列IsActive
を簡単に想像できます。
ALTER TABLE dbo.AccountsContactsXRef
ADD IsActive BIT NOT NULL
CONSTRAINT DF_AccountsContactsXRef_IsActive
DEFAULT ((1));
CREATE INDEX IX_AccountsContactsXRef_IsActive
ON dbo.AccountsContactsXRef(IsActive)
WHERE IsActive = 1;
いいえ、外部キーの循環参照を持つことはできません。制約を絶えず削除して再作成しないとデータを挿入できないためだけではありません。しかし、それは私が考えることができるあらゆるドメインの根本的に欠陥のあるモデルであるためです。あなたの例では、アカウントと連絡先の関係がN-Nではなく、アカウントと連絡先の両方へのFK参照を持つジャンクションテーブルが必要なドメインは考えられません。
CREATE TABLE Account
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50)
)
CREATE TABLE Contact
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50),
)
CREATE TABLE AccountContact
(
AccountID INT FOREIGN KEY REFERENCES Account(ID),
ContactID INT FOREIGN KEY REFERENCES Contact(ID),
primary key(AccountID,ContactID)
)
外部オブジェクトをアカウントではなく、主要な連絡先にポイントさせることができます。データは次のようになります。
CREATE TABLE Account
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50)
)
CREATE TABLE Contact
(
ID INT PRIMARY KEY IDENTITY,
Name VARCHAR(50),
AccountID INT FOREIGN KEY REFERENCES Account(ID)
)
CREATE TABLE AccountOwner (
Other Stuff Here . . .
PrimaryContactID INT FOREIGN KEY REFERENCES Contact(ID)
)