2008-12-01 66 views
9

Giả sử tôi có bảng đại diện cho một lớp siêu, sinh viên. Và sau đó tôi có N bảng đại diện cho các lớp con của đối tượng đó (vận động viên, nhạc sĩ, v.v ...). Làm thế nào tôi có thể thể hiện một ràng buộc sao cho một học sinh phải được mô hình hóa trong một lớp con (không nhiều hơn, không ít hơn)?Duy trì tính toàn vẹn của lớp con trong cơ sở dữ liệu quan hệ

Làm rõ về ý kiến:

  • này đã được duy trì bằng tay, không thông qua một gói ORM.
  • Dự án này liên quan đến việc ngồi trên máy chủ SQL (nhưng sẽ rất tuyệt khi xem giải pháp chung)
  • Đây có thể không phải là ví dụ tốt nhất. Có một vài tình huống chúng ta có thể xem xét liên quan đến phân lớp, và tôi đã tình cờ phát minh ra ví dụ sinh viên/vận động viên này.

A) Theo kiểu hướng đối tượng thực sự, có thể lớp cha có thể tồn tại và không cần phải được mô hình hóa trong bất kỳ lớp con nào.

B) Trong cuộc sống thực, bất kỳ đối tượng hoặc học sinh nào đều có thể có nhiều vai trò.

C) Kịch bản cụ thể mà tôi đã cố gắng minh họa là yêu cầu mọi đối tượng phải được thực hiện chính xác trong một lớp con. Hãy nghĩ về siêu lớp như một sự triển khai trừu tượng, hoặc chỉ các tính phổ biến được tạo ra từ các lớp/cá thể đối tượng khác nhau.

Cảm ơn tất cả những gì bạn đã nhập, đặc biệt là Bill.

+0

Chỉ cần làm rõ: Bạn có quản lý thủ công điều này hoặc sử dụng giải pháp ORM như ngủ đông không? – Uri

+0

Bạn đang sử dụng cơ sở dữ liệu nào? Nếu bạn đang sử dụng PostgreSQL, nó có bảng kế thừa có sẵn. – Elijah

+0

Tôi rất tò mò, Tại sao không thể trở thành một vận động viên và một nhạc sĩ? –

Trả lời

2

Dưới đây là một vài khả năng. Một là CHECK trong mỗi bảng mà student_id không xuất hiện trong bất kỳ bảng phụ con nào khác. Điều này có thể tốn kém và mỗi lần bạn cần một loại phụ mới, bạn cần phải sửa đổi ràng buộc trong tất cả các bảng hiện có.

CREATE TABLE athletes (
    student_id INT NOT NULL PRIMARY KEY, 
    FOREIGN KEY (student_id) REFERENCES students(student_id), 
    CHECK (student_id NOT IN (SELECT student_id FROM musicians 
         UNION SELECT student_id FROM slackers 
         UNION ...)) 
); 

chỉnh sửa: @JackPDouglas một cách chính xác chỉ ra rằng hình thức trên các ràng buộc kiểm tra không được hỗ trợ bởi Microsoft SQL Server. Trên thực tế, nó cũng không hợp lệ theo tiêu chuẩn SQL-99 để tham khảo một bảng khác (xem http://kb.askmonty.org/v/constraint_type-check-constraint).

SQL-99 định nghĩa đối tượng siêu dữ liệu cho các ràng buộc nhiều bảng. Điều này được gọi là ASSERTION, tuy nhiên tôi không biết bất kỳ RDBMS nào thực hiện xác nhận.

Có lẽ cách tốt hơn là đặt khóa chính trong bảng students một khóa chính phức hợp, cột thứ hai biểu thị một loại phụ. Sau đó, hạn chế cột đó trong mỗi bảng con thành một giá trị tương ứng với loại con được biểu diễn bởi bảng. chỉnh sửa: không cần phải tạo PK thành khóa ghép trong bảng con.

CREATE TABLE athletes (
    student_id INT NOT NULL PRIMARY KEY, 
    student_type CHAR(4) NOT NULL CHECK (student_type = 'ATHL'), 
    FOREIGN KEY (student_id, student_type) REFERENCES students(student_id, student_type) 
); 

Tất nhiên student_type có thể chỉ là một cách dễ dàng là một số nguyên, tôi chỉ thấy nó như là một char cho mục đích minh hoạ.

Nếu bạn không có hỗ trợ cho các ràng buộc CHECK (ví dụ: MySQL), thì bạn có thể làm điều gì đó tương tự trong trình kích hoạt.

Tôi đọc phần tiếp theo của bạn về việc đảm bảo hàng tồn tại trong một số bảng lớp con cho mỗi hàng trong bảng siêu lớp. Tôi không nghĩ rằng có một cách thực tế để làm điều này với SQL siêu dữ liệu và hạn chế. Tùy chọn duy nhất tôi có thể đề xuất để đáp ứng yêu cầu này là sử dụng Single-Table Inheritance. Nếu không, bạn cần phải dựa vào mã ứng dụng để thực thi nó.

chỉnh sửa: JackPDouglas cũng đề xuất sử dụng thiết kế dựa trên Class Table Inheritance. Xem his example hoặc ví dụ của tôi về kỹ thuật tương tự here hoặc here hoặc here.

+1

-1 cho thông tin gây hiểu lầm về ràng buộc kiểm tra. Từ [tài liệu SQL Server 2008R2] (http://msdn.microsoft.com/en-us/library/ms174979 (v = SQL.105).aspx): "Điều kiện tìm kiếm phải đánh giá biểu thức Boolean và không thể tham chiếu đến bảng khác." –

+0

Tôi không nhận được tín dụng để đề xuất một điều gì đó khác sau đó? Vâng, dù sao, cảm ơn vì đã giải thích cho bạn, hầu hết mọi người không làm điều đó. –

+0

Nếu giải pháp ban đầu của bạn * đã * hoạt động, nó sẽ là một lựa chọn hấp dẫn. Tôi nghĩ Jack ban đầu đã thu hút được giải pháp đầu tiên của bạn, chỉ để thất vọng vì đã lãng phí thời gian của mình vì nó dường như không được hỗ trợ. 1 để chỉnh sửa câu trả lời của bạn :-) –

0

vấn đề thú vị. Tất nhiên những hạn chế FK là có cho subtables vì ​​vậy có phải là một sinh viên cho những người.

Vấn đề chính đang cố kiểm tra khi được chèn. Trước tiên, học sinh phải được chèn vào để bạn không vi phạm ràng buộc FK trong một subtable vì vậy một kích hoạt mà một kiểm tra sẽ không hoạt động.

Bạn có thể viết một ứng dụng kiểm tra ngay bây giờ và sau đó nếu bạn thực sự lo ngại về điều này. Tôi nghĩ rằng nỗi sợ hãi lớn nhất mặc dù sẽ bị xóa. Ai đó có thể xóa một mục nhập có thể trừ nhưng không phải là sinh viên. Bạn có thể kích hoạt để kiểm tra khi các mục bị xóa khỏi các subtables vì ​​đó có lẽ là vấn đề lớn nhất.

Tôi có một db với một bảng cho mỗi phân cấp lớp con như thế này. Tôi sử dụng Hibernate và ánh xạ của nó đúng cách để nó xóa tất cả mọi thứ tự động. Nếu làm điều này bằng 'tay' thì tôi sẽ đảm bảo luôn luôn xóa phụ huynh với các thác thích hợp hehe :)

0

Xin cảm ơn, Bill. Bạn đã cho tôi suy nghĩ ...

Bảng siêu lớp có cột mã lớp con. Mỗi bảng con có một ràng buộc khóa ngoài, cũng như một bảng chỉ ra rằng id tồn tại với một tập hợp con của bảng siêu lớp (trong đó mã = ​​vận động viên).

Phần còn thiếu duy nhất ở đây là có thể mô hình hóa một siêu lớp không có lớp con. Ngay cả khi bạn làm cho cột mã bắt buộc, nó chỉ có thể là một tham gia rỗng. Điều đó có thể được sửa bằng cách thêm một ràng buộc rằng các id của lớp cha tồn tại trong một liên minh của các id trong các bảng lớp con. Việc chèn thêm một chút lông với hai ràng buộc này nếu các ràng buộc được thực thi ở giữa các giao dịch. Điều đó hoặc chỉ không lo lắng về các đối tượng chưa được phân loại.

Chỉnh sửa: Bleh, một ý tưởng hay như vậy ... Nhưng bị cản trở bởi thực tế là các truy vấn con tham chiếu đến các bảng khác không được hỗ trợ. Ít nhất không phải trong SQL Server.

0

Điều đó có thể được khắc phục bằng cách thêm một ràng buộc rằng id của lớp cha tồn tại trong một liên kết các id trong bảng phân lớp.

Tùy thuộc vào mức độ thông minh bạn muốn đưa vào lược đồ (và MS SQL Server cho phép bạn đặt bao nhiêu), bạn sẽ không thực sự cần phải kết hợp các bảng phân lớp, vì bạn biết rằng , nếu id tồn tại trong bất kỳ bảng lớp con nào, nó phải tồn tại trong cùng một lớp con như một lớp được xác định bởi cột mã lớp con.

+0

(a) bạn không thể truy vấn bảng khác trong một ràng buộc trong MS SQL Server, và (b) bạn không thể truy vấn một bảng có tên phụ thuộc vào một giá trị dữ liệu. –

3

Mỗi bản ghi Sinh viên sẽ có cột SubClass (giả sử vì lý do đó là CHAR (1)). {A = Vận động viên, M = nhạc sĩ ...}

Bây giờ, hãy tạo các bảng Vận động viên và Nhạc sĩ của bạn.Họ cũng nên có một cột SubClass, nhưng phải có một ràng buộc kiểm tra mã hóa cứng giá trị cho loại bảng mà chúng đại diện. Ví dụ, bạn nên đặt mặc định 'A' và ràng buộc CHECK của 'A' cho cột SubClass trên bảng Athlete.

Liên kết Nhạc sĩ của bạn và bảng Athlete vào bảng Student sử dụng một chìa khóa nước ngoài COMPOSITE của StudentID VÀ Subclass. Và bạn đã hoàn tất! Hãy thưởng thức một tách cà phê ngon.

CREATE TABLE Student (
    StudentID INT NOT NULL IDENTITY PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Name VARCHAR(200) NOT NULL, 
    CONSTRAINT UQ_Student UNIQUE (StudentID, SubClass) 
); 

CREATE TABLE Athlete (
    StudentID INT NOT NULL PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Sport VARCHAR(200) NOT NULL, 
    CONSTRAINT CHK_Jock CHECK (SubClass = 'A'), 
    CONSTRAINT FK_Student_Athlete FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass) 
); 

CREATE TABLE Musician (
    StudentID INT NOT NULL PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Instrument VARCHAR(200) NOT NULL, 
    CONSTRAINT CHK_Band_Nerd CHECK (SubClass = 'M'), 
    CONSTRAINT FK_Student_Musician FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass) 
); 
+0

+1. Điều này thực sự làm việc. – BobbyShaftoe

1

Nếu bạn quan tâm đến việc tạo mô hình dữ liệu, ngoài mô hình đối tượng, tôi khuyên bạn nên tìm kiếm "chuyên môn hóa mô hình hoá quan hệ" trên web.

Đã từng có một số tài nguyên tốt ngoài đó giải thích loại mẫu này khá tốt.

Tôi hy vọng các tài nguyên đó vẫn còn ở đó.

Dưới đây là một cái nhìn đơn giản về những gì tôi hy vọng bạn sẽ tìm thấy.

Trước khi bắt đầu thiết kế một cơ sở dữ liệu, nó là hữu ích để tìm ra một mô hình dữ liệu khái niệm kết nối các giá trị được lưu trữ trong cơ sở dữ liệu trở lại với vấn đề. Tạo mô hình dữ liệu khái niệm thực sự là phân tích dữ liệu, chứ không phải thiết kế cơ sở dữ liệu. Đôi khi rất khó để phân tích và thiết kế riêng biệt.

Một cách để xây dựng mô hình dữ liệu ở cấp độ khái niệm là Entity-Mối quan hệ (ER) mô hình. Có những mô hình nổi tiếng để mô hình hoá tình huống chuyên môn hóa. Việc chuyển đổi các mẫu ER đó thành các bảng SQL (được gọi là thiết kế logic) là khá đơn giản, mặc dù bạn phải thực hiện một số lựa chọn thiết kế.

Trường hợp bạn đã cho của một học sinh có khả năng một số vai trò như nhạc sĩ có lẽ không minh họa cho trường hợp bạn quan tâm, nếu tôi đọc bạn đúng. Bạn dường như quan tâm đến trường hợp các lớp con loại trừ lẫn nhau. Có lẽ trường hợp chiếc xe có thể là ô tô, xe tải hoặc xe máy có thể dễ dàng thảo luận hơn.

Một sự khác biệt bạn có khả năng gặp phải là bảng chung cho các lớp cha không thực sự cần cột loại mã. Loại của một cá thể siêu lớp đơn có thể được dẫn xuất bởi sự hiện diện hoặc vắng mặt của các khoá ngoại trong các bảng phân lớp khác nhau. Cho dù thông minh hơn để bao gồm hay bỏ qua mã loại phụ thuộc vào cách bạn dự định sử dụng dữ liệu.

0

Tôi có thể thêm Ràng buộc kiểm tra.
Tạo khóa ngoại tuyến là Nullable. Thêm một Kiểm tra để đảm bảo rằng chúng không phải là cả hai giá trị rỗng và để đảm bảo chúng không được đặt cả hai. CONSTRAINT [CK_HasOneForiegnKey] CHECK ((FK_First! = NULL HOẶC FK_Second! = NULL) VÀ NOT (FK_First! = NULL VÀ FK_Second! = NULL)).

Tôi không chắc chắn nhưng tôi tin rằng điều này sẽ cho phép bạn thiết lập chỉ có một chìa khóa cùng một lúc.

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