22

Giả sử bạn đã thiết lập cơ sở dữ liệu để lưu trữ dữ liệu thử nghiệm sự cố của các loại xe khác nhau. Bạn muốn lưu trữ dữ liệu của các bài kiểm tra sự cố cho tàu cao tốc, xe hơi và go-kart.Điều gì đó giống như kế thừa trong thiết kế cơ sở dữ liệu

Bạn có thể tạo ba bảng riêng biệt: SpeedboatTests, CarTests và GokartTests. Nhưng nhiều cột của bạn sẽ giống nhau trong mỗi bảng (ví dụ: id nhân viên của người thực hiện kiểm tra, hướng của xung đột (phía trước, bên, phía sau), v.v.). Tuy nhiên, nhiều cột sẽ khác nhau, vì vậy bạn không muốn chỉ cần đặt tất cả dữ liệu thử nghiệm trong một bảng vì bạn sẽ có một vài cột sẽ luôn là rỗng cho tàu cao tốc, một vài cột sẽ luôn luôn không có giá trị cho xe hơi, và một số ít sẽ không có giá trị cho go-kart.

Giả sử bạn cũng muốn lưu trữ một số thông tin không liên quan trực tiếp đến các kiểm tra (chẳng hạn như id nhân viên của nhà thiết kế của điều đang được kiểm tra). Các cột này dường như không được đặt trong bảng "Kiểm tra", đặc biệt là vì chúng sẽ được lặp lại cho tất cả các thử nghiệm trên cùng một phương tiện.

Hãy để tôi minh họa một sắp xếp bàn có thể, do đó bạn có thể xem các câu hỏi liên quan.

 
Speedboats 
id | col_about_speedboats_but_not_tests1 | col_about_speedboats_but_not_tests2 

Cars 
id | col_about_cars_but_not_tests1 | col_about_cars_but_not_tests2 

Gokarts 
id | col_about_gokarts_but_not_tests1 | col_about_gokarts_but_not_tests2 

Tests 
id | type | id_in_type | col_about_all_tests1 | col_about_all_tests2 
(id_in_type will refer to the id column of one of the next three tables, 
depending on the value of type) 

SpeedboatTests 
id | speedboat_id | col_about_speedboat_tests1 | col_about_speedboat_tests2 

CarTests 
id | car_id | col_about_car_tests1 | col_about_car_tests2 

GokartTests 
id | gokart_id | col_about_gokart_tests1 | col_about_gokart_tests2 

Điều gì là tốt/xấu về cấu trúc này và cách nào là cách ưa thích để triển khai một cái gì đó như thế này?

Điều gì sẽ xảy ra nếu có một số thông tin áp dụng cho tất cả các xe bạn muốn có trong bảng Phương tiện? Bảng CarTests có trông giống như ...

 
id | vehicle_id | ... 

With a Vehicles table like this: 
id | type | id_in_type 
(with id_in_type pointing to the id of either a speedboat, car, or go-kart) 

Đây chỉ là một mớ hỗn độn của hoàng gia. Làm thế nào NÊN một cái gì đó như thế này được thiết lập?

+0

bản sao có thể có của [Làm thế nào để bạn có hiệu quả mô hình kế thừa trong cơ sở dữ liệu?] (Http://stackoverflow.com/questions/190296/how-do-you-effectively-model-inheritance-in-a-database) – Musa

Trả lời

37

Thiết kế typeid_in_type được gọi là Polymorphic Associations. Thiết kế này phá vỡ các quy tắc chuẩn hóa theo nhiều cách. Nếu không có gì khác, nó phải là cờ đỏ mà bạn không thể khai báo ràng buộc khóa ngoài thực sự, vì id_in_type có thể tham chiếu đến bất kỳ một số bảng nào.

Đây là một cách tốt hơn về việc xác định bảng của bạn:

  • Hãy một bảng tóm tắt Vehicles để cung cấp một điểm tham chiếu trừu tượng cho tất cả các xe sub-loại và kiểm tra xe.
  • Mỗi loại phụ xe có khóa chính không tự động tăng, nhưng thay vào đó hãy tham khảo Vehicles.
  • Mỗi loại phụ thử nghiệm có khóa chính không tự động tăng, nhưng thay vào đó hãy tham khảo Tests.
  • Mỗi loại phụ thử nghiệm cũng có khóa ngoại cho loại phụ xe tương ứng.

Dưới đây là mẫu DDL:

CREATE TABLE Vehicles (
vehicle_id INT AUTO_INCREMENT PRIMARY KEY 
); 

CREATE TABLE Speedboats (
vehicle_id INT PRIMARY KEY, 
col_about_speedboats_but_not_tests1 INT, 
col_about_speedboats_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Cars (
vehicle_id INT PRIMARY KEY, 
col_about_cars_but_not_tests1 INT, 
col_about_cars_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Gokarts (
vehicle_id INT PRIMARY KEY, 
col_about_gokarts_but_not_tests1 INT, 
col_about_gokarts_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Tests (
test_id INT AUTO_INCREMENT PRIMARY KEY, 
col_about_all_tests1 INT, 
col_about_all_tests2 INT 
); 

CREATE TABLE SpeedboatTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_speedboat_tests1 INT, 
col_about_speedboat_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Speedboats(vehicle_id) 
); 

CREATE TABLE CarTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_car_tests1 INT, 
col_about_car_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Cars(vehicle_id) 
); 

CREATE TABLE GokartTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_gokart_tests1 INT, 
col_about_gokart_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Gokarts(vehicle_id) 
); 

Hoặc bạn có thể tuyên bố Tests.vehicle_id mà tham chiếu Vehicles.vehicle_id và thoát khỏi các phím nước ngoài vehicle_id trong mỗi bảng kiểm tra sub-type, nhưng điều đó sẽ cho phép bất thường, chẳng hạn như một kiểm tra tàu cao tốc tham chiếu đến id của gokart.

+0

Điều này cực kỳ hữu ích và kỹ lưỡng. Cảm ơn bạn! –

+2

tất cả các câu trả lời khác ngoại trừ câu trả lời này và, có thể, [đề cập đến Martin Fowler] (http://stackoverflow.com/a/554552/279564), cần được loại bỏ hoặc chôn vùi vào quên lãng ... OMG .. – Rafa

+0

Thanks @ Rafa! Chúc mừng –

0

Tôi sẽ chia nhỏ thành các bảng khác nhau, ví dụ: Xe (ID, loại, vv) VehicleAttributes() VehicleID, AttributeID, Value), CrashTestInfo (VehicleID, CrashtestID, Date, vv) CrashtestAttributes (CrashTestID, AttributeID, Value)

Hoặc thay vì các thuộc tính, các bảng riêng biệt cho mỗi bộ các chi tiết tương tự cần được ghi lại.

+0

Đó là thiết kế Entity-Attribute-Value, là quá mức cần thiết cho kịch bản của OP. –

14

Để lập bản đồ phân cấp thừa kế cho các bảng cơ sở dữ liệu, tôi nghĩ Martin Fowler đưa ra các lựa chọn thay thế khá tốt trong cuốn sách Mẫu Kiến trúc ứng dụng doanh nghiệp của mình.

http://martinfowler.com/eaaCatalog/singleTableInheritance.html

http://martinfowler.com/eaaCatalog/classTableInheritance.html

http://martinfowler.com/eaaCatalog/concreteTableInheritance.html

Nếu số lượng thêm các trường/cột là nhỏ so với các lớp con, sau đó thừa kế bảng duy nhất thường là đơn giản nhất để đối phó với.

Nếu bạn đang sử dụng PostgreSQL cho cơ sở dữ liệu của bạn và bạn sẵn sàng để buộc mình vào một tính năng cơ sở dữ liệu cụ thể, nó hỗ trợ bảng thừa kế trực tiếp:

http://www.postgresql.org/docs/8.3/static/ddl-inherit.html

+0

Tôi muốn thêm rằng với tham chiếu cụ thể đến mớ hỗn độn của hoàng gia ám chỉ trong câu hỏi ban đầu rằng khóa ngoại sẽ trỏ từ loại xe cụ thể sang bảng xe trừu tượng. tức là tàu cao tốc (vehicle_id FK, speedboat_specific_column1, v.v ...) – Robin

-3

thiết kế của bạn là hợp lý và đang theo các quy tắc chuẩn hóa chính xác. Bạn có thể thiếu một bảng Xe với một Id và Loại Xe (tức là "cha mẹ" cho Speedboats, Ô tô và Gokarts ... nơi bạn muốn giữ những thứ như "DesignedByUserId").Giữa bảng Xe và bảng Speedboats là mối quan hệ một - một - một, và giữa Xe và Tàu cao tốc/Xe hơi/GoKarts có mối quan hệ 1-và-1 (tức là một chiếc xe chỉ có thể có 1 kỷ lục cho tàu cao tốc, xe ô tô hoặc đi kart) ... mặc dù hầu hết các db không cung cấp một cơ chế thực thi dễ dàng cho việc này.

Một quy tắc chuẩn hóa giúp xác định các loại điều này là trường chỉ phụ thuộc vào khóa chính của bảng. Trong một bảng tổng hợp, nơi kết quả kiểm tra tàu cao tốc, ô tô và gokart được lưu trữ cùng nhau thì các trường liên quan đến ô tô không chỉ phụ thuộc vào ngày thi mà còn phụ thuộc vào id và loại phương tiện. Khóa chính cho bảng kết quả kiểm tra là ngày thử nghiệm + id xe và loại phương tiện không phải là yếu tố khiến hàng dữ liệu thử nghiệm duy nhất (nghĩa là vẫn có thể tiến hành thử nghiệm vào 01/01/200912: 30pm trên một phương tiện cụ thể đó là cả tàu cao tốc và xe hơi ... không ... không thể làm được).

Tôi không giải thích quy tắc bình thường một cách cụ thể ... nhưng quy tắc biểu mẫu bình thường thứ 3/4/5 luôn gây nhầm lẫn cho tôi khi tôi đọc các mô tả chính thức. Một trong số đó (thứ 3/thứ 4/thứ 5) giao dịch với các trường tùy thuộc vào khóa chính và chỉ có khóa chính. Quy tắc này giả định rằng khóa chính đã được xác định chính xác (không chính xác xác định khóa chính quá dễ làm).

+1

-1 vì thiết kế liên kết đa hình (điều 'loại' và' id_in_type') là * không * thiết kế chuẩn hóa. –

+0

Uhmm ... xem http://en.wikipedia.org/wiki/Fourth_normal_form. Ví dụ về bánh pizza khá hợp lý. – user53794

+1

Bạn đang nói {test_id, type} -> -> {id_in_type} chuyển 4NF, do đó {test_id, type} là siêu dữ liệu? Tôi đang nói về định nghĩa cơ bản của một quan hệ, trong đó mỗi thuộc tính đại diện cho một giá trị cho một "điều" - nhưng id_in_type là ba loại khác nhau của sự vật. –

0

Thực hiện tìm kiếm trên google về "mô hình hóa quan hệ gen-spec". Bạn sẽ tìm thấy các bài viết về cách thiết lập các bảng lưu trữ các thuộc tính của thực thể tổng quát (những gì các lập trình viên OO có thể gọi là siêu lớp), các bảng riêng biệt cho từng thực thể chuyên biệt (các lớp con) và cách sử dụng các khóa ngoài để liên kết nó tất cả cùng nhau.

Các bài viết hay nhất, IMO, thảo luận về gen-spec về mô hình ER. Nếu bạn biết cách dịch mô hình ER thành mô hình quan hệ, và từ đó đến các bảng SQL, bạn sẽ biết phải làm gì khi chúng cho bạn thấy cách mô hình gen-spec trong ER.

Nếu bạn chỉ google trên "gen-spec", hầu hết những gì bạn sẽ thấy là hướng đối tượng, không định hướng quan hệ. Điều đó có thể hữu ích là tốt, miễn là bạn biết làm thế nào để vượt qua trở kháng đối tượng quan hệ không phù hợp.

+2

Sẽ rất tuyệt nếu bạn có thể cung cấp một số liên kết trực tiếp. – JamesC

+0

Đây chỉ là phương pháp tiếp cận bảng lớp (như được tham chiếu trong câu trả lời được chấp nhận và tham chiếu Fowler) – oligofren

0

Nếu bạn đang sử dụng SQLAlchemy, một trình ánh xạ đối tượng cho Python, bạn có thể configure how inheritance hierarchies are mapped to database tables. Các trình lập bản đồ quan hệ đối tượng là tốt cho việc thuần hóa SQL tẻ nhạt.

Sự cố của bạn có thể phù hợp với các bảng dọc. Thay vì lưu trữ mọi thứ trong lược đồ, lưu trữ kiểu của đối tượng và khóa chính trong một bảng và các giá trị khóa/giá trị cho từng đối tượng trong một bảng khác. Nếu bạn thực sự đã lưu trữ kiểm tra xe hơi, thiết lập này sẽ giúp bạn thêm các loại kết quả mới dễ dàng hơn nhiều.

Các vấn đề liên quan