Karena Anda menggunakan bidang yang dapat dibatalkan untuk kunci asing, Anda sebenarnya dapat membangun sistem yang berfungsi dengan benar seperti yang Anda bayangkan. Untuk memasukkan baris ke dalam tabel Akun, Anda harus memiliki baris yang ada di tabel Kontak kecuali Anda mengizinkan sisipan ke dalam Akun dengan null PrimaryContactID. Untuk membuat baris kontak tanpa harus memiliki baris Akun, Anda harus membiarkan kolom AccountID di tabel Kontak menjadi nullable. Ini memungkinkan Akun tidak memiliki kontak, dan memungkinkan Kontak tidak memiliki akun. Mungkin ini diinginkan, mungkin tidak.
Karena itu, preferensi pribadi saya adalah memiliki pengaturan berikut:
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;
Ini memberikan kemampuan untuk:
- Gambarkan dengan jelas hubungan antara kontak dan akun melalui tabel referensi silang seperti yang direkomendasikan Pieter dalam jawabannya
- Pertahankan integritas referensial dengan cara yang sehat dan tidak melingkar.
- Berikan daftar kontak utama yang sangat terpelihara melalui
IX_AccountsContactsXRef_Primary
indeks. Indeks ini berisi filter, sehingga hanya akan berfungsi pada platform yang mendukungnya. Karena indeks ini ditentukan dengan UNIQUE
opsi, hanya ada satu kontak utama untuk setiap akun.
Misalnya, jika Anda ingin menampilkan daftar semua kontak, dengan kolom yang menunjukkan status "primer", menampilkan kontak utama di bagian atas daftar untuk setiap Akun, Anda dapat melakukan:
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;
Indeks yang difilter mencegah penyisipan lebih dari satu kontak utama per akun, sementara secara bersamaan memberikan metode cepat untuk mengembalikan daftar kontak utama. Seseorang dapat dengan mudah membayangkan kolom lain, IsActive
dengan indeks yang disaring tidak unik untuk mempertahankan riwayat kontak per akun, bahkan setelah kontak itu tidak lagi dikaitkan dengan akun:
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;