Tôi nghĩ @ a1ex07 đang đi đúng hướng tại đây (+1). Tôi không nghĩ rằng những khoảng trống trong itemOrder
vi phạm 3NF, nhưng tôi lo lắng về một sự vi phạm khác của 3NF (thêm về điều này bên dưới). Chúng tôi cũng phải xem ra dữ liệu xấu trong trường itemOrder
. Dưới đây là cách tôi bắt đầu:
create table pages (
pid int,
primary key (pid)
);
create table users (
uid int,
primary key (uid)
);
create table items (
iid int,
primary key (iid)
);
create table details (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
itemOrder int,
primary key (pid, uid, iid),
unique (pid, uid, itemOrder)
);
Khóa chính đảm bảo cho mỗi trang, cho mỗi người dùng, có các mục duy nhất. Ràng buộc duy nhất đảm bảo rằng đối với mỗi trang, đối với mỗi người dùng, có các mục OrderOrders duy nhất. Đây là lo lắng của tôi về 3NF: trong trường hợp này, itemOrder
không hoàn toàn phụ thuộc vào khóa chính; nó chỉ phụ thuộc vào các phần (pid, uid)
. Đó không phải là 2NF; và đó là một vấn đề. Chúng tôi có thể bao gồm itemOrder
trong khóa chính, nhưng sau đó tôi lo lắng rằng nó có thể không phải là tối thiểu, như PK cần phải được. Chúng ta có thể cần phải phân hủy nó thành nhiều bảng hơn. Vẫn đang nghĩ . . .
[EDIT - Suy nghĩ thêm về chủ đề. . . ]
Giả
Có người sử dụng.
Có các trang.
Có các mục.
(trang, người dùng) xác định một SET mục.
(trang, người dùng) xác định DANH MỤC các khe mà chúng tôi có thể lưu trữ các mặt hàng nếu chúng tôi muốn.
Chúng tôi không muốn có các mục trùng lặp trong danh sách (trang, người dùng).
Plan A
Giết bảng details
ở trên.
Thêm bảng, ItemsByPageAndUser
, để đại diện cho SET mục được xác định bởi (trang, người dùng).
create table ItemsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
primary key (pid, uid, iid)
)
Thêm bảng, SlotsByPageAndUser
, để thể hiện DANH MỤC của các vị trí có thể chứa các mục.
create table SlotsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
slotNum int not null,
iidInSlot int references items(iid),
primary key (pid, uid, slotNum),
foreign key (pid, uid, iid) references ItemsByPageAndUser(pid, uid, iid),
unique (pid, uid, iid)
)
Note 1: iidInSlot
là nullable để chúng tôi có thể có khe rỗng nếu chúng ta muốn. Nhưng nếu có một món quà thì nó phải được kiểm tra đối với bảng vật phẩm.
Lưu ý 2: Chúng tôi cần FK cuối cùng để đảm bảo rằng chúng tôi không thêm bất kỳ mục nào không nằm trong tập hợp các mục có thể cho (người dùng, trang) này.
Lưu ý 3: Ràng buộc duy nhất trên (pid, uid, iid)
thi hành mục tiêu thiết kế của chúng tôi về việc có các mục duy nhất trong danh sách (giả định 6).Nếu không có điều này, chúng tôi có thể thêm bao nhiêu mục từ tập hợp được xác định bởi (trang, người dùng) như chúng tôi muốn miễn là chúng nằm trong các vị trí khác nhau.
Bây giờ chúng tôi đã tách riêng các mục khỏi các vị trí của chúng trong khi vẫn duy trì sự phụ thuộc chung của chúng trên (trang, người dùng).
Thiết kế này chắc chắn trong 3NF và có thể ở BCNF, mặc dù tôi lo lắng về số SlotsByPageAndUser
trong lĩnh vực đó.
Vấn đề là do ràng buộc duy nhất trong bảng SlotsByPageAndUser
tính bản số của mối quan hệ giữa SlotsByPageAndUser
và ItemsByPageAndUser
là một-một. Nói chung, các mối quan hệ 1-1 không phải là các kiểu thực thể sai. Có những ngoại lệ, tất nhiên, và có lẽ đây là một. Nhưng có thể có một cách tốt hơn. . .
Plan B
Giết bảng SlotsByPageAndUser
.
Thêm một cột slotNum
vào ItemsByPageAndUser
.
Thêm một ràng buộc duy nhất trên (pid, uid, iid)
đến ItemsByPageAndUser
.
Bây giờ là:
create table ItemsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
slotNum int,
primary key (pid, uid, iid),
unique (pid, uid, slotNum)
)
Chú giải 4: Rời slotNum
nullable bảo tồn khả năng của chúng tôi để xác định các mục trong tập không có trong danh sách. Nhưng . . .
Lưu ý 5: Đặt ràng buộc duy nhất trên biểu thức liên quan đến cột có thể vô hiệu có thể gây ra kết quả "thú vị" trong một số cơ sở dữ liệu. Tôi nghĩ nó sẽ hoạt động như chúng tôi dự định trong Postgres. (Xem this discussion ở đây trên SO.) Đối với các cơ sở dữ liệu khác, số dặm của bạn có thể thay đổi.
Bây giờ không có mối quan hệ 1-1 lộn xộn nào xảy ra xung quanh, vì vậy tốt hơn. Nó vẫn là 3NF là thuộc tính không khóa duy nhất (slotNum
) phụ thuộc vào khóa, toàn bộ khóa và không có gì ngoài khóa. (Bạn không thể hỏi về slotNum
mà không nói với tôi những gì trang, người sử dụng, và mục mà bạn đang nói về.)
Nó không BCNF vì [(pid, uid, iid)
->slotNum
] và [(pid,uid,slotNum)
->iid
]. Nhưng đó là lý do tại sao chúng tôi có ràng buộc duy nhất trên (pid, uid, slotNum) ngăn không cho dữ liệu xâm nhập vào trạng thái không nhất quán.
Tôi nghĩ đây là giải pháp khả thi.
Tại sao không chỉ xóa/chèn danh sách mới với chỉ mục tuần tự mới? – Xailor
Trong thực tế, số lần người dùng chanegs thứ tự? Và bạn mong đợi bao nhiêu mục cho mỗi người dùng? – a1ex07
Dữ liệu ghi lại thứ tự của một cái gì đó là không có cách nào "chống lại các nguyên tắc của một cơ sở dữ liệu quan hệ". Vấn đề là trong một cơ sở dữ liệu quan hệ, thứ tự không phải là vốn có trong cấu trúc * của cơ sở dữ liệu. Do đó, trong một cơ sở dữ liệu quan hệ, thông tin * phải * luôn luôn được lưu trữ như các giá trị của các thuộc tính trong các bộ tuples trong các quan hệ - đó chính xác là những gì bạn đang đề xuất. – sqlvogel