Поскольку вы используете пустые поля для внешних ключей, вы можете создать систему, которая будет работать правильно, как вы ее себе представляете. Для вставки строк в таблицу «Учетные записи» необходимо, чтобы в таблице «Контакты» присутствовала строка, если только вы не разрешаете вставки в «Учетные записи» с нулевым PrimaryContactID. Чтобы создать строку контакта, в которой еще нет строки «Учетная запись», необходимо разрешить использование столбца «AccountID» в таблице «Контакты». Это позволяет Учетным записям не иметь контактов, и позволяет Контактам не иметь учетной записи. Возможно это желательно, возможно нет.
Сказав это, мое личное предпочтение будет иметь следующую настройку:
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опции, для каждой учетной записи может быть только один основной контакт.
Например, если вы хотите отобразить список всех контактов с колонкой, обозначающей «основной» статус, с указанием основных контактов в верхней части списка для каждой учетной записи, вы можете сделать следующее:
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;