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