Оскільки ви використовуєте нульові поля для зовнішніх ключів, ви можете насправді побудувати систему, яка працює правильно, як ви її уявляли. Для того, щоб вставити рядки в таблицю облікових записів, вам потрібно мати рядок, присутній у таблиці "Контакти", якщо ви не дозволяєте вставляти в облікові записи з нульовим 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;