Cấu trúc phù hợp cho kịch bản này là mô hình SubClass / Thừa kế và gần giống với khái niệm tôi đề xuất trong câu trả lời này: Danh sách giá trị không đồng nhất theo thứ tự .
Mô hình được đề xuất trong câu hỏi này thực sự khá gần gũi ở chỗ Animalthực thể chứa loại (tức là race) và các thuộc tính phổ biến trên tất cả các loại. Tuy nhiên, có hai thay đổi nhỏ cần thiết:
Xóa các trường Cat_ID và Dog_ID khỏi các thực thể tương ứng của chúng:
Các khái niệm then chốt ở đây là tất cả mọi thứ là một Animal, không phân biệt race: Cat, Dog, Elephant, và vân vân. Cho rằng điểm bắt đầu, bất kỳ đặc biệt racecủa Animalkhông thực sự cần một định danh riêng biệt từ:
- sự
Animal_IDđộc đáo
- các
Cat, Dogvà bất kỳ thêm racecác đơn vị bổ sung trong tương lai không, bởi bản thân, đầy đủ đại diện cho bất kỳ đặc biệt Animal; chúng chỉ có ý nghĩa khi được sử dụng kết hợp với thông tin có trong thực thể mẹ , Animal.
Do đó, Animal_IDbất động sản trong Cat, Dog, vv thực thể là cả PK và mặt sau FK đến Animalthực thể.
Phân biệt giữa các loại breed:
Chỉ vì hai thuộc tính có cùng tên không nhất thiết có nghĩa là các thuộc tính đó giống nhau, ngay cả khi tên giống nhau ngụ ý mối quan hệ như vậy. Trong trường hợp này, những gì bạn thực sự phải là thực sự CatBreedvà DogBreedlà "loại" riêng biệt
Ghi chú ban đầu
- SQL dành riêng cho Microsoft SQL Server (tức là T-SQL). Có nghĩa là, hãy cẩn thận về các kiểu dữ liệu vì chúng không giống nhau trên tất cả các RDBMS. Ví dụ, tôi đang sử dụng
VARCHARnhưng nếu bạn cần lưu trữ bất cứ thứ gì bên ngoài bộ ASCII tiêu chuẩn, bạn thực sự nên sử dụng NVARCHAR.
- Các trường ID của các bảng "loại" (
Race, CatBreedvà DogBreed) không tự động tăng (nghĩa là IDENTITY theo T-SQL) vì chúng là các hằng số ứng dụng (nghĩa là chúng là một phần của ứng dụng) là các giá trị tra cứu tĩnh trong cơ sở dữ liệu và được biểu diễn dưới dạng enums trong C # (hoặc các ngôn ngữ khác). Nếu các giá trị được thêm vào, chúng được thêm vào trong các tình huống được kiểm soát. Tôi bảo lưu việc sử dụng các trường tăng tự động cho dữ liệu người dùng đi qua ứng dụng.
- Quy ước đặt tên tôi sử dụng là đặt tên cho mỗi bảng lớp con bắt đầu bằng tên lớp chính theo sau là tên lớp con. Điều này giúp tổ chức các bảng cũng như chỉ ra rõ ràng (không cần nhìn vào FK) mối quan hệ của bảng lớp con với bảng thực thể chính.
- Vui lòng xem phần "Chỉnh sửa cuối cùng" ở cuối để biết ghi chú về Lượt xem.
"Giống" là "Chủng tộc" - Cách tiếp cận cụ thể

Nhóm bảng đầu tiên này là bảng tra cứu / loại:
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
);
Danh sách thứ hai này là thực thể "Động vật" chính:
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);
Điều này thiết lập thứ ba của bảng là các thực thể sub-class miễn phí mà hoàn thành định nghĩa của mỗi Racecủa 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);
Mô hình sử dụng breedloại chia sẻ được hiển thị sau phần "Ghi chú bổ sung".
Ghi chú bổ sung
- Khái niệm
breeddường như là một tiêu điểm cho sự nhầm lẫn. Nó được đề xuất bởi jcolebrand (trong một bình luận về câu hỏi) đó breedlà một tài sản được chia sẻ trên các races khác nhau và hai câu trả lời khác đã được tích hợp như vậy trong các mô hình của họ. Tuy nhiên, đây là một lỗi vì các giá trị breedkhông được chia sẻ trên các giá trị khác nhau của race. Có, tôi biết rằng hai mô hình đề xuất khác cố gắng giải quyết vấn đề này bằng cách làm racecha mẹ của breed. Mặc dù về mặt kỹ thuật giải quyết được vấn đề về mối quan hệ, nó không giúp giải quyết câu hỏi mô hình hóa tổng thể về những việc cần làm đối với các thuộc tính không phổ biến cũng như cách xử lý một racevấn đề không có breed. Nhưng, trong trường hợp một tài sản như vậy được đảm bảo tồn tại trên tất cảAnimals, tôi cũng sẽ bao gồm một tùy chọn cho điều đó (bên dưới).
- Các mô hình được đề xuất bởi vijayp và DavidN (dường như giống hệt nhau) không hoạt động vì:
- Họ hoặc
- không cho phép các thuộc tính không phổ biến được lưu trữ (ít nhất là không cho các trường hợp riêng lẻ của bất kỳ
Animal), hoặc
- yêu cầu tất cả các thuộc tính cho tất cả các
races được lưu trữ trong Animalthực thể đó là một cách rất đơn giản (và gần như không liên quan) để biểu diễn dữ liệu này. Có, mọi người làm điều này mọi lúc, nhưng điều đó có nghĩa là có nhiều trường NULL trên mỗi hàng cho các thuộc tính không dành cho cụ thể đó racevà biết trường nào trên mỗi hàng được liên kết với cụ thể racecủa bản ghi đó.
- Họ không cho phép thêm một
racesố Animaltrong tương lai mà không có breednhư một tài sản. Và thậm chí nếu TẤT CẢ Animals có một breed, điều đó sẽ không thay đổi cấu trúc do những gì trước đây đã được ghi nhận về breed: đó breedlà phụ thuộc vào race(tức là breedcho Catkhông phải là điều tương tự như breedcho Dog).
"Giống" như cách tiếp cận tài sản chung / / chung

Xin lưu ý:
SQL bên dưới có thể được chạy trong cùng một cơ sở dữ liệu như mô hình được trình bày ở trên:
- Cái
Racebàn giống nhau
- Cái
Breedbàn mới
- Ba
Animalbảng đã được thêm vào một2
- Ngay cả
Breedkhi là một tài sản chung hiện nay, có vẻ như không được Racelưu ý trong thực thể chính / cha mẹ (ngay cả khi nó đúng về mặt kỹ thuật). Vì vậy, cả hai RaceIDvà BreedIDđược đại diện trong Animal2. Để ngăn chặn sự không phù hợp giữa RaceIDghi chú trong Animal2và BreedIDkhác biệt RaceID, tôi đã thêm một FK trên cả hai RaceID, BreedIDtham chiếu CONSTRAINT ĐỘC ĐÁO của các trường đó trong Breedbảng. Tôi thường coi thường việc chỉ ra một FK cho một CONSTRAINT ĐỘC ĐÁO, nhưng đây là một trong số ít lý do hợp lệ để làm điều đó. CONSTRAINT ĐỘC ĐÁO là một "Khóa thay thế" một cách hợp lý, làm cho nó hợp lệ cho việc sử dụng này. Cũng xin lưu ý rằng Breedbảng vẫn có PK BreedID.
- Lý do không chỉ xảy ra với PK trên các trường kết hợp và không có CONSTRAINT UNIITE là vì nó sẽ cho phép
BreedIDlặp lại cùng một giá trị khác nhau RaceID.
- Lý do không chuyển đổi PK và UNIQUE CONSTRAINT xung quanh là vì đây có thể không phải là cách sử dụng duy nhất
BreedID, vì vậy vẫn có thể tham chiếu một giá trị cụ thể Breedmà không có RaceIDsẵn.
- Mặc dù mô hình sau không hoạt động, nó có hai lỗ hổng tiềm năng liên quan đến khái niệm chia sẻ
Breed(và đó là lý do tại sao tôi thích các bảng đặc Racethù Breed).
- Có một giả định ngầm định rằng TẤT CẢ các giá trị
Breedcó cùng thuộc tính. Không có cách dễ dàng nào trong mô hình này để có các thuộc tính khác nhau giữa Dog"giống" và Elephant"giống". Tuy nhiên, vẫn có một cách để làm điều này, được ghi chú trong phần "Chỉnh sửa cuối cùng".
- Không có cách nào để chia sẻ
Breednhiều hơn một chủng tộc. Tôi không chắc chắn nếu điều đó là mong muốn để làm (hoặc có thể không phải trong khái niệm động vật nhưng có thể trong các tình huống khác sẽ sử dụng loại mô hình này), nhưng không thể ở đây.
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);
Chỉnh sửa cuối cùng (hy vọng ;-)
- Về khả năng (và khó khăn sau đó) xử lý các thuộc tính khác nhau giữa các loại
Breed, nó là có thể sử dụng các lớp con / thừa kế khái niệm tương tự nhưng với Breedlà thực thể chính. Trong thiết lập này, Breedbảng sẽ có các thuộc tính chung cho tất cả các loại Breed(giống như Animalbảng) và RaceIDsẽ đại diện cho loại Breed(giống như trong Animalbảng). Sau đó, bạn sẽ có các bảng lớp con như BreedCat, BreedDogv.v. Đối với các dự án nhỏ hơn, điều này có thể được coi là "kỹ thuật quá mức", nhưng nó đang được đề cập như là một lựa chọn cho các tình huống sẽ được hưởng lợi từ nó.
Đối với cả hai cách tiếp cận, đôi khi nó giúp tạo Chế độ xem dưới dạng rút gọn cho các thực thể đầy đủ. Ví dụ: xem xét:
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"
- Mặc dù không phải là một phần của các thực thể logic, nhưng điều khá phổ biến là có các trường kiểm toán trong các bảng để ít nhất có được ý nghĩa khi các bản ghi được chèn và cập nhật. Vì vậy, trong điều kiện thực tế:
- Một
CreatedDatetrường sẽ được thêm vào Animalbảng. Trường này không cần thiết trong bất kỳ bảng lớp con nào (ví dụ AnimalCat) vì các hàng được chèn cho cả hai bảng phải được thực hiện cùng một lúc trong một giao dịch.
- Một
LastModifiedDatetrường sẽ được thêm vào Animalbảng và tất cả các bảng lớp con. Trường này chỉ được cập nhật nếu bảng cụ thể đó được cập nhật: nếu một bản cập nhật xảy ra AnimalCatnhưng không có trong Animalmột cụ thể AnimalID, thì chỉ có LastModifiedDatetrường trong AnimalCatđó sẽ được đặt.