web-dev-qa-db-ja.com

循環外部キー参照があっても問題ありませんか?

外部キーフィールドの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)
29
KidCode

外部キーに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;

これにより、次のことが可能になります。

  1. ピーターが彼の回答で推奨する方法である相互参照表を通じて、連絡先と取引先の関係を明確に描きます
  2. 参照整合性を適切な非循環的な方法で維持します。
  3. 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;
12
Max Vernon

いいえ、外部キーの循環参照を持つことはできません。制約を絶えず削除して再作成しないとデータを挿入できないためだけではありません。しかし、それは私が考えることができるあらゆるドメインの根本的に欠陥のあるモデルであるためです。あなたの例では、アカウントと連絡先の関係が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)
)
5
Pieter Geerkens

外部オブジェクトをアカウントではなく、主要な連絡先にポイントさせることができます。データは次のようになります。

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