Struktur yang tepat untuk skenario ini adalah model SubClass / Inheritance, dan hampir identik dengan konsep yang saya usulkan dalam jawaban ini: Daftar nilai yang diurutkan secara heterogen .
Model yang diajukan dalam pertanyaan ini sebenarnya cukup dekat karena Animalentitas berisi tipe (yaitu race) dan properti yang umum di semua tipe. Namun, ada dua perubahan kecil yang diperlukan:
Hapus bidang Cat_ID dan Dog_ID dari entitasnya masing-masing:
Konsep kunci di sini adalah bahwa segala sesuatu adalah Animal, terlepas dari race: Cat, Dog, Elephant, dan sebagainya. Mengingat titik awal itu, setiap bagian racedari Animaltidak benar-benar memerlukan pengidentifikasi terpisah karena:
- yang
Animal_IDunik
- yang
Cat, Dog, dan setiap tambahan raceentitas ditambahkan di masa depan tidak, dengan sendirinya, sepenuhnya mewakili tertentu Animal; mereka hanya memiliki makna ketika digunakan dalam kombinasi dengan informasi yang terkandung dalam entitas induk Animal,.
Oleh karena itu, Animal_IDproperti di Cat, Dog, dll entitas adalah baik PK dan kembali FK ke Animalentitas.
Bedakan antara jenis breed:
Hanya karena dua properti memiliki nama yang sama tidak selalu berarti bahwa properti itu sama, bahkan jika nama yang sama menyiratkan hubungan seperti itu. Dalam hal ini, apa yang sebenarnya Anda miliki sebenarnya CatBreeddan DogBreedsebagai "tipe" terpisah
Catatan Awal
- SQL khusus untuk Microsoft SQL Server (yaitu T-SQL). Artinya, berhati-hatilah dengan tipe data karena mereka tidak sama di semua RDBMS. Sebagai contoh, saya menggunakan
VARCHARtetapi jika Anda perlu menyimpan sesuatu di luar set ASCII standar, Anda harus benar-benar menggunakannya NVARCHAR.
- Bidang ID dari tabel "type" (
Race,, CatBreeddan DogBreed) tidak bertambah secara otomatis (yaitu IDENTITAS dalam hal T-SQL) karena mereka adalah konstanta aplikasi (yaitu mereka adalah bagian dari aplikasi) yang merupakan nilai pencarian statis di database dan direpresentasikan sebagai enums di C # (atau bahasa lainnya). Jika nilai ditambahkan, mereka ditambahkan dalam situasi terkontrol. Saya mencadangkan penggunaan bidang penambahan otomatis untuk data pengguna yang masuk melalui aplikasi.
- Konvensi penamaan yang saya gunakan adalah memberi nama setiap tabel subclass dimulai dengan nama kelas utama diikuti oleh nama subclass. Ini membantu mengatur tabel serta menunjukkan dengan jelas (tanpa melihat FK) hubungan tabel subkelas dengan tabel entitas utama.
- Silakan lihat bagian "Edit Terakhir" di bagian akhir untuk catatan tentang Tampilan.
"Breed" sebagai "Race" -Pendekatan Khusus

Kumpulan tabel pertama ini adalah tabel lookup / types:
CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE CatBreed
(
CatBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
CatBreedAttribute1 INT,
CatBreedAttribute2 VARCHAR(10)
-- other "CatBreed"-specific properties as needed
);
CREATE TABLE DogBreed
(
DogBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
DogBreedAttribute1 TINYINT
-- other "DogBreed"-specific properties as needed
);
Daftar kedua ini adalah entitas "Hewan" utama:
CREATE TABLE Animal
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
Name VARCHAR(50)
-- other "Animal" properties that are shared across "Race" types
);
ALTER TABLE Animal
ADD CONSTRAINT [FK_Animal_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
Seperangkat tabel ketiga ini adalah entitas sub-kelas gratis yang melengkapi definisi masing Race- masing Animal:
CREATE TABLE AnimalCat
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
CatBreedID INT NOT NULL, -- FK to CatBreed
HairColor VARCHAR(50) NOT NULL
-- other "Cat"-specific properties as needed
);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_CatBreed]
FOREIGN KEY (CatBreedID)
REFERENCES CatBreed (CatBreedID);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
CREATE TABLE AnimalDog
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
DogBreedID INT NOT NULL, -- FK to DogBreed
HairColor VARCHAR(50) NOT NULL
-- other "Dog"-specific properties as needed
);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_DogBreed]
FOREIGN KEY (DogBreedID)
REFERENCES DogBreed (DogBreedID);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
Model menggunakan breedtipe bersama ditampilkan setelah bagian "Catatan Tambahan".
catatan tambahan
- Konsep
breedtampaknya menjadi titik fokus untuk kebingungan. Itu disarankan oleh jcolebrand (dalam komentar pada pertanyaan) yang breedmerupakan properti yang dibagikan di antara yang berbeda race, dan dua jawaban lainnya memilikinya terintegrasi seperti dalam model mereka. Namun, ini merupakan kesalahan, karena nilai untuk breedtidak dibagi di antara nilai yang berbeda dari race. Ya, saya menyadari bahwa dua model lain yang diusulkan berupaya menyelesaikan masalah ini dengan menjadikannya racesebagai induk breed. Sementara itu secara teknis memecahkan masalah hubungan, itu tidak membantu memecahkan pertanyaan pemodelan keseluruhan tentang apa yang harus dilakukan tentang properti yang tidak umum, atau bagaimana menangani raceyang tidak memiliki a breed. Tapi, dalam hal properti seperti itu dijamin ada di semuaAnimals, saya akan menyertakan opsi untuk itu juga (di bawah).
- Model yang diusulkan oleh vijayp dan DavidN (yang tampaknya identik) tidak berfungsi karena:
- Mereka juga
- jangan izinkan properti non-umum disimpan (setidaknya tidak untuk masing-masing instance
Animal), atau
- mengharuskan semua properti untuk semua
racedisimpan dalam Animalentitas yang merupakan cara yang sangat datar (dan hampir non-relasional) untuk mewakili data ini. Ya, orang melakukan ini sepanjang waktu, tetapi itu artinya memiliki banyak bidang NULL per baris untuk properti yang tidak dimaksudkan untuk bidang tertentu tersebut raceDAN mengetahui bidang mana per baris yang terkait dengan catatan tertentu race.
- Mereka tidak memungkinkan untuk menambahkan
racedari Animaldalam waktu yang tidak memiliki breedsebagai properti. Dan bahkan jika SEMUA Animalmemiliki breed, itu tidak akan mengubah struktur karena apa yang telah dicatat sebelumnya breed: yang breedbergantung pada race(yaitu breeduntuk Catbukan hal yang sama seperti breeduntuk Dog).
"Breed" sebagai Pendekatan Properti Bersama / Bersama

Tolong dicatat:
SQL di bawah ini dapat dijalankan dalam database yang sama dengan model yang disajikan di atas:
- The
Racemeja adalah sama
- The
Breedtabel baru
- Tiga
Animaltabel telah ditambahkan dengan2
- Bahkan dengan
Breedmenjadi properti yang sekarang umum, tampaknya tidak benar untuk tidak Racemencatat dalam entitas induk / induk (meskipun secara teknis benar secara relasional). Jadi, keduanya RaceIDdan BreedIDdiwakili dalam Animal2. Untuk mencegah ketidaksesuaian antara yang RaceIDdicatat dalam Animal2dan BreedIDyang berbeda RaceID, saya telah menambahkan FK pada keduanya RaceID, BreedIDyang merujuk KONSTRA UNIK dari bidang-bidang dalam Breedtabel. Saya biasanya membenci menunjuk seorang FK ke CONSTRAINT UNIK, tetapi di sini adalah salah satu dari beberapa alasan yang sah untuk melakukannya. KONSTRA UNIK secara logis merupakan "Kunci Alternatif", yang membuatnya valid untuk penggunaan ini. Harap perhatikan juga bahwa Breedtabel masih memiliki PK hanya BreedID.
- Alasan untuk tidak pergi hanya dengan PK pada bidang gabungan dan tidak ada KONSTRA UNIK adalah bahwa hal itu akan memungkinkan hal yang sama
BreedIDdiulang di berbagai nilai RaceID.
- Alasan untuk tidak beralih yang PK dan KONSTRA UNIK sekitar adalah bahwa ini mungkin bukan satu-satunya penggunaan
BreedID, jadi masih mungkin untuk referensi nilai tertentu Breedtanpa memiliki yang RaceIDtersedia.
- Sementara model berikut ini berfungsi, ia memiliki dua kelemahan potensial terkait konsep shared
Breed(dan itulah sebabnya saya lebih suka tabel Race-specific Breed).
- Ada asumsi implisit bahwa SEMUA nilai
Breedmemiliki sifat yang sama. Tidak ada cara mudah dalam model ini untuk memiliki sifat yang berbeda antara Dog"breed" dan Elephant"breed". Namun, masih ada cara untuk melakukan ini, yang dicatat di bagian "Edit Akhir".
- Tidak ada cara untuk membagikan
Breedlebih dari satu ras. Saya tidak yakin apakah itu diinginkan untuk dilakukan (atau mungkin tidak dalam konsep hewan tetapi mungkin dalam situasi lain yang akan menggunakan model jenis ini), tetapi tidak mungkin di sini.
CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY,
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE Breed
(
BreedID INT NOT NULL PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
BreedName VARCHAR(50)
);
ALTER TABLE Breed
ADD CONSTRAINT [UQ_Breed]
UNIQUE (RaceID, BreedID);
ALTER TABLE Breed
ADD CONSTRAINT [FK_Breed_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
CREATE TABLE Animal2
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race, FK to Breed
BreedID INT NOT NULL, -- FK to Breed
Name VARCHAR(50)
-- other properties common to all "Animal" types
);
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Breed]
FOREIGN KEY (RaceID, BreedID)
REFERENCES Breed (RaceID, BreedID);
CREATE TABLE AnimalCat2
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalCat2
ADD CONSTRAINT [FK_AnimalCat2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
CREATE TABLE AnimalDog2
(
AnimalID INT NOT NULL PRIMARY KEY,
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalDog2
ADD CONSTRAINT [FK_AnimalDog2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
Sunting Akhir (semoga ;-)
- Mengenai kemungkinan (dan kemudian kesulitan) menangani sifat yang berbeda antara jenis
Breed, adalah mungkin untuk menggunakan konsep subclass / warisan yang sama tetapi dengan Breedsebagai entitas utama. Dalam pengaturan ini Breedtabel akan memiliki sifat-sifat yang umum untuk semua jenis Breed(seperti Animaltabel) dan RaceIDakan mewakili jenis Breed(sama seperti halnya dalam Animaltabel). Maka Anda akan memiliki tabel subclass seperti BreedCat, BreedDog, dan sebagainya. Untuk proyek yang lebih kecil ini mungkin dianggap "rekayasa berlebihan", tetapi sedang disebutkan sebagai pilihan untuk situasi yang akan mendapat manfaat dari itu.
Untuk kedua pendekatan, terkadang membantu untuk membuat Tampilan sebagai jalan pintas ke entitas penuh. Sebagai contoh, pertimbangkan:
CREATE VIEW Cats AS
SELECT an.AnimalID,
an.RaceID,
an.Name,
-- other "Animal" properties that are shared across "Race" types
cat.CatBreedID,
cat.HairColor
-- other "Cat"-specific properties as needed
FROM Animal an
INNER JOIN AnimalCat cat
ON cat.AnimalID = an.AnimalID
-- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
- Meskipun bukan bagian dari entitas logis, cukup umum untuk memiliki bidang audit dalam tabel untuk setidaknya memahami kapan catatan dimasukkan dan diperbarui. Jadi secara praktis:
- Sebuah
CreatedDatelapangan akan ditambahkan ke Animalmeja. Bidang ini tidak diperlukan dalam tabel subkelas mana pun (misalnya AnimalCat) karena baris yang dimasukkan untuk kedua tabel harus dilakukan pada waktu yang sama dalam suatu transaksi.
- Sebuah
LastModifiedDatelapangan akan ditambahkan ke Animalmeja dan semua tabel subclass. Bidang ini akan diperbarui hanya jika tabel tertentu diperbarui: jika pembaruan terjadi AnimalCattetapi tidak dalam Animaluntuk tertentu AnimalID, maka hanya LastModifiedDatebidang yang AnimalCatakan ditetapkan.