2010-03-11 37 views
5

Tôi có thể có cột "danh tính" (duy nhất, không lặp lại) trong nhiều bảng không? Ví dụ: giả sử tôi có hai bảng: Sách và tác giả.Tôi có thể tạo một trường nhận dạng bao trùm nhiều bảng trong SQL Server không?

Authors 
    AuthorID 
    AuthorName 
Books 
    BookID 
    BookTitle 

Cột BookID và cột AuthorID là cột nhận dạng. Tôi muốn phần danh tính bao trùm cả hai cột. Vì vậy, nếu có một AuthorID có giá trị là 123, thì không thể có một BookID có giá trị là 123. Và ngược lại.

Tôi hy vọng điều đó có ý nghĩa.

Điều này có khả thi không?

Cảm ơn.

Tại sao tôi muốn thực hiện việc này? Tôi đang viết một ứng dụng APS.NET MVC. Tôi đang tạo một phần bình luận. Tác giả có thể có ý kiến. Sách có thể có nhận xét. Tôi muốn có thể chuyển một ID tổ chức (ID cuốn sách hoặc ID tác giả) cho một hành động và có hành động kéo lên tất cả các nhận xét tương ứng. Hành động sẽ không quan tâm nếu đó là một cuốn sách hay một tác giả hay bất cứ điều gì. Âm thanh hợp lý?

Trả lời

2

Câu trả lời ngắn gọn là: Không, bạn không thể làm điều đó (ít nhất là trong MS SQL Server đến năm 2008). Bạn có thể tạo một bảng mới, "CommentableEntity", cắm cột nhận dạng của bạn vào đó, sau đó xác định các khóa ngoài trong Tác giả và Sách để tham chiếu nó dưới dạng bảng cha và sau đó thực hiện một trong các thủ thuật để đảm bảo rằng một giá trị ID đã cho không được gán cho cả hai bảng ...nhưng đây là một ý tưởng tồi, bởi vì mô hình dữ liệu bạn xây dựng sẽ ngụ ý rằng Tác giả và Sách là các loại dữ liệu có liên quan và chúng thực sự không.

Bạn có thể có một bảng riêng biệt, Nhận xét, có cột nhận dạng trong đó và đỗ cột CommentId trong cả Tác giả và Sách. Tuy nhiên, điều đó sẽ giới hạn mỗi cuốn sách và tác giả chỉ một bình luận.

Tôi, tôi có thể thêm cột như "CommentorType" vào bảng Nhận xét và đặt cờ trong đó cho biết nguồn nhận xét ("A" cho tác giả "B" cho sách). Tạo khóa chính trên "CommentorId + CommentorType" và nó sẽ hoạt động đủ tốt - và nó sẽ không quan trọng để thêm các loại bình luận khác khi hệ thống mở rộng.

+0

Bạn không thể có một điểm khóa ngoài duy nhất cho hai bảng khác nhau, ngay cả với cờ tiện dụng. –

+0

Đề xuất của bạn ("Tôi có thể thêm cột như" CommentorType "vào Nhận xét") là đường dẫn tôi đã đi trước khi quyết định đăng và đảm bảo không có cách nào dễ dàng hơn để thực hiện. Cảm ơn. – johnnycakes

+0

Nhưng thêm loại bình luận vào các bình luận không phải là một giải pháp tốt! Đừng làm thế! Bạn sẽ hối tiếc! OK, bây giờ tôi cảm thấy khá hơn. Tiếp tục đi. –

0

Là một đề xuất - hãy thử sử dụng bảng như ComentId, EntityId, isBook, Bình luận cho nhận xét. isBook là loại boolean và không có nhiều nơi để có được. Khái niệm của bạn không tốt từ quan điểm quan hệ.

5

Thậm chí nếu bạn có thể đặt chuỗi danh tính trên nhiều bảng, bảng nhận xét của bạn sẽ không thể tham chiếu cả hai cột trong một khóa ngoại.

Cách tốt nhất để làm điều này, về lý thuyết thiết kế cơ sở dữ liệu quan hệ, sẽ là tạo hai bảng nhận xét. Nhưng rõ ràng, bạn muốn tránh điều đó, có lẽ vì lý do tái sử dụng mã.

Cách tiếp cận thực dụng đơn giản nhất sẽ là đặt hai cột khóa ngoài trên bảng nhận xét và chỉ tạo một giá trị rỗng và cột kia không rỗng cho mỗi nhận xét.

Một cách tiếp cận khác, có thể là sự thỏa hiệp tốt nhất, chính là điều này. Bạn tham khảo câu hỏi của mình với "ID tổ chức". Vì vậy, tạo một bảng Entity! Sau đó, các tác giả và sách và nhận xét đều có thể tham khảo rằng bảng.

Edited thêm:

Philip Kelley, Ray, và (tôi nghĩ) Artic đều đề nghị sửa đổi các bảng bình luận bằng cách thêm một entity_id, có thể tham khảo một trong hai book_id hoặc author_id, và một cờ của một số loại (char(1), tinyintboolean tương ứng) cho biết loại nào trong số này đang được đề cập đến.

Đây không phải là giải pháp tốt vì nhiều lý do, cả thực dụng (bao gồm toàn vẹn dữ liệu, báo cáo, hiệu quả) và lý thuyết.

Vấn đề đầu tiên và rõ ràng nhất là vấn đề toàn vẹn dữ liệu. Một hệ thống cơ sở dữ liệu quan hệ phải luôn chịu trách nhiệm duy trì tính toàn vẹn của dữ liệu riêng của nó, và có những cách tự nhiên và ưu tiên mà DB được thiết kế để làm điều này. Một trong những cơ chế quan trọng nhất là hệ thống khóa ngoại. Nếu cột comment.entity_id là tham chiếu cả hai book.book_idauthor.author_id thì không thể tạo khóa ngoại cho cột này.Chắc chắn, bạn có thể đặt một kiểm tra trong các thủ tục lưu trữ DML của bạn (chèn, cập nhật, xóa) để xác minh các tham chiếu, nhưng điều đó sẽ nhanh chóng trở thành một mớ hỗn độn lớn, vì tất cả các hoạt động DML trên cả ba bảng sẽ được tham gia.

Và điều đó dẫn chúng ta đến vấn đề hiệu quả. Bất cứ khi nào truy vấn chạy trên bảng comment, nó sẽ yêu cầu tham gia vào bảng author hoặc book hoặc cả hai. Hệ thống tạo kế hoạch truy vấn sẽ không có sẵn các khóa ngoại để tối ưu hóa, vì vậy hiệu năng của nó có thể bị suy giảm rất tốt.

Sau đó, có vấn đề với lược đồ này trong báo cáo. Bất kỳ hệ thống tạo báo cáo nào cũng sẽ gặp sự cố với loại hệ thống này. Chắc chắn đây sẽ không phải là vấn đề đối với các lập trình viên chuyên gia, nhưng bất kỳ báo cáo đặc biệt nào của người dùng sẽ phải giả lập logic đằng sau khi event_id có nghĩa là điều này hoặc điều đó, và nó có thể là một vấn đề khá xấu. Có thể bạn sẽ không bao giờ sử dụng các công cụ tạo báo cáo trên cơ sở dữ liệu này. Nhưng sau đó một lần nữa, không ai biết nơi mà một cơ sở dữ liệu sẽ được sử dụng cuối cùng. Tại sao không làm việc với hệ thống để cho phép bất cứ điều gì?

Và điều đó dẫn chúng ta đến các vấn đề lý thuyết.

Trong lý thuyết cơ sở dữ liệu quan hệ, mỗi hàng (a.k.a. "tuple") trong mỗi bảng ("biến quan hệ") đại diện cho một mệnh đề về thế giới thực. Thiết kế một bảng là quyết định hình thức của đề xuất đó. Hãy xem xét một vài ví dụ về cách thức hoạt động của nó.

comment (comment_id int, comment_type char(1), entity_id int, 
     user_id int, comment_text nvarchar(max), comment_date datetime) 
/* comment_id identifies a comment (comment_text) that a user (user_id) 
    has made about a book (entity_id if comment_type = 'B') or author 
    (entity_id if comment_type = 'A') at a particular date and 
    time (comment_date).*/ 

Ở đây rõ ràng rằng cột (hoặc "thuộc tính") được gọi là entity_id đang thực hiện nhiệm vụ kép. Nó không thực sự đại diện cho bất cứ điều gì, ngoại trừ với tham chiếu đến cột khác. Điều này là hoàn toàn khả thi, nhưng không đạt yêu cầu.

comment (comment_id int, book_id int, author_id int, user_id int, 
     comment_text nvarchar(max), comment_date datetime) 
/* comment_id identifies a comment (comment_text) that a user (user_id) 
    has made about a book (book_id if not null) or author (author_id if 
    not null) at a particular date and time (comment_date). */ 

Điều này sẽ mua cho chúng tôi khóa ngoại là sự thiếu sót lớn nhất từ ​​phiên bản đầu tiên. Nhưng điều này vẫn không đủ khả quan, trừ khi một bình luận duy nhất có thể đề cập đến cả một cuốn sách và một tác giả (có thể là hợp lý). Các cột dễ vỡ là một dấu hiệu cảnh báo rằng có điều gì đó không ổn với thiết kế và cũng có thể là trường hợp ở đây. Một ràng buộc kiểm tra có thể là cần thiết để tránh một nhận xét không liên quan đến gì cả, hoặc cả sách và tác giả nếu điều đó không được phép.

Từ góc độ lý thuyết (và do đó, quan điểm của tôi :)) có một lựa chọn rõ ràng nhất:

book_comment (book_comment_id int, book_id int, user_id int, 
       comment_text nvarchar(max), comment_date datetime) 
/* book_comment_id identifies a comment (comment_text) that a 
    user (user_id) has made about a book (book_id) at a particular 
    date and time (comment_date). */ 

author_comment (author_comment_id int, author_id int, user_id int, 
       comment_text nvarchar(max), comment_date datetime) 
/* author_comment_id identifies a comment (comment_text) that a 
    user (user_id) has made about an author (author_id) at a particular 
    date and time (comment_date). */ 

này Tùy chọn cuối cùng sẽ cung cấp hiệu suất tốt nhất, toàn vẹn dữ liệu, và dễ báo cáo. Và chi phí duy nhất là các thủ tục được lưu trữ của DML sẽ cần đưa các nhận xét vào các bảng bên phải, đó không phải là một vấn đề lớn, bởi vì họ phải biết những gì các bình luận đang đề cập đến.

Nếu kế hoạch của bạn là lấy lại tất cả các nhận xét cho một cuốn sách hoặc tác giả cùng một lúc, bạn có thể dễ dàng tạo ra một khung nhìn phía trên các bảng này sao chép các thiết kế khác, nếu đó là những gì bạn muốn làm.

create view comments as 
select 
    book_comment_id as comment_id, 
    book_id as entity_id, 
    comment_text, 
    'B' as comment_type 
from book_comment 
union 
select 
    author_comment_id as comment_id, 
    author_id as entity_id, 
    comment_text, 
    'A' as comment_type 
from author_comment 
+0

Xin chào Jeffrey, Tại sao bạn nghĩ ý tưởng xấu là sử dụng "ID loại người nhận xét" cùng với "ID tổ chức"? Tại sao hai đề xuất sau này của bạn tốt hơn? Tôi vẫn đang học! Cảm ơn. – johnnycakes

+0

OK. Tôi đã giải quyết câu hỏi của bạn trong bản chỉnh sửa cho câu trả lời. –

+0

Đây, IMO, là câu trả lời đúng. Sử dụng cấu trúc EAV cho loại giải pháp này là câu trả lời sai và sẽ biến xấu trong báo cáo. Việc thêm một bảng khác thực sự không tốn nhiều nhưng cung cấp nhiều lợi ích bao gồm khả năng nhận xét của tác giả sau này có các thuộc tính mà các chú thích sách không có. – Thomas

0

Máy chủ SQL không hỗ trợ điều này. Bạn có thể cuộn của riêng bạn với một bảng id, nhưng đó sẽ là công việc nhiều hơn nó là giá trị.

Tôi đề nghị bảng nhận xét của bạn trông như thế này:

comment_id int identity 
comment_type tinyint 
entity_id int 

comment_type định nếu những nhận xét thuộc về một cuốn sách, một tác giả, hay cái gì khác bạn thêm trong tương lai. entity_id là id của cuốn sách, tác giả, bất cứ điều gì. Trong chương trình này, không quan trọng nếu sách hoặc id tác giả trùng lặp.

Hoặc, nếu bạn có thể chuyển sang oracle, sử dụng một chuỗi :)

1

Trên thực tế, Joe Celko gợi ý trên this blog để sử dụng một chuỗi tùy chỉnh trong cơ sở dữ liệu của bạn, và sau đó, đối với bất kỳ khóa chính của bảng mong muốn của bạn , chỉ định các giá trị mặc định của chúng để nhận số tiếp theo từ chuỗi tùy chỉnh của bạn.

Đây là một mẫu mã từ blog của mình:

CREATE SEQUENCE Service_Ticket_Seq 
AS INTEGER 
START WITH 1 
INCREMENT BY 1 
MINVALUE 1 
MAXVALUE 100 
CYCLE; 

CREATE TABLE Meats 
(ticket_seq INTEGER DEFAULT NEXT VALUE FOR Service_Ticket_Seq 
     PRIMARY KEY, 
meat_type VARCHAR(15) NOT NULL); 

CREATE TABLE Fish 
(ticket_seq INTEGER DEFAULT NEXT VALUE FOR Service_Ticket_Seq 
     PRIMARY KEY, 
fish_type VARCHAR(15) NOT NULL); 

INSERT INTO Meats (meat_type) VALUES ('pig'); 
INSERT INTO Fish (fish_type) VALUES ('squid'); 

select * from Meats 

select * from Fish 

này đang được nói, một lĩnh vực nhận dạng kéo dài nhiều bảng có thể có trong MS SQL.

+0

Có, nó hoạt động trong Microsoft SQL Server 2014 –

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