2012-03-02 36 views
10

Tôi biết rằng loại này đi ngược lại các nguyên tắc của một cơ sở dữ liệu quan hệ nhưng hãy để tôi mô tả tình huống.Biểu diễn tốt nhất về danh sách được sắp xếp trong cơ sở dữ liệu?

Tôi có một trang nơi người dùng sẽ đặt một số mục.

________________ 
| -Item1   | 
| -Item2   | 
| -Item3   | 
| -Item4   | 
|________________| 

Các mục này phải theo thứ tự người dùng cung cấp cho họ. Tuy nhiên, lệnh này có thể được thay đổi một số lần tùy ý bởi người dùng.

________________ 
| -Item1   | 
| -Item4   | 
| -Item2   | 
| -Item3   | 
|________________| 

Cách tiếp cận 1

suy nghĩ ban đầu của tôi là cung cấp cho các mục một chỉ số đại diện cho vị trí của họ trong danh sách

Page   Item 
----------- --------------- 
FK | pid  FK | pid 
    | name  PK | iid 
        | index 
        | content 

Với giải pháp này, bạn có thể chọn các mục where pid = Page.pidorder by index mà thuận tiện. Tuy nhiên mỗi khi bạn thay đổi thứ tự bạn phải thay đổi bất cứ nơi nào giữa một mục khác (trường hợp tốt nhất) và tất cả các mục khác (trường hợp xấu nhất).

Cách tiếp cận 2

Tôi cũng coi thực hiện một "danh sách liên kết" như cấu trúc dữ liệu mà mỗi mục điểm đến mục tiếp theo trong danh sách.

Page   Item 
----------- --------------- 
FK | pid  FK | pid 
    | name  PK | iid 
        | next 
        | content 

Điều này có khả năng làm thay đổi thứ tự rẻ hơn nhưng chúng tôi phải dựa vào lập trình giao diện người dùng để trích xuất đơn đặt hàng.

Có cách tiếp cận nào mà tôi chưa từng nghĩ đến không? Làm ơn cho tôi biết.

+0

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

+0

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

+5

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

Trả lời

5

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ả

  1. Có người sử dụng.

  2. Có các trang.

  3. Có các mục.

  4. (trang, người dùng) xác định một SET mục.

  5. (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.

  6. 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 SlotsByPageAndUserItemsByPageAndUser 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

  1. Giết bảng SlotsByPageAndUser.

  2. Thêm một cột slotNum vào ItemsByPageAndUser.

  3. 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.

+0

Gói B là giống như bài đăng gốc? Ít nhất đó là những gì tôi thấy. – Mallow

1

Bạn có thể thêm một nhân vật (nvarchar) cột mới vào bảng Page gọi order có chứa một danh sách phân của iid 's theo thứ tự bạn thích, ví dụ: 1,4,3,2. Ưu điểm chỉ là một trường trong một bảng để duy trì - nhược điểm rõ ràng là cần phải viết một hàm tiện ích để chuyển đổi giữa các ký tự và kiểu số trong thực tế có thể sẽ không mất quá nhiều thời gian.

+0

Tôi không hiểu tại sao đây là một ý tưởng tồi. Có nhiều vấn đề với việc sắp xếp lại các mục trong cơ sở dữ liệu quan hệ; điều này có một trang ngay từ NoSQL và giải quyết nó một cách nhanh chóng. Tôi đồng ý nó có thể không được tốt đẹp như vậy, nhưng nó giải quyết vấn đề một cách nhanh chóng. –

3

Nếu bạn mong đợi số lượng mục không lớn, bạn có thể sử dụng phiên bản sửa đổi bit của phương pháp đầu tiên. Chỉ cần tạo khoảng cách giữa các chỉ mục liên tiếp. Ví dụ, mục đầu tiên có chỉ số 100, thứ hai 200, vv Bằng cách này bạn không cần phải cập nhật tất cả các chỉ số mọi thời gian, chỉ khi bạn không thể tìm thấy một khoảng cách

+0

Đây là một cái gì đó tôi đã xem xét (một ý tưởng còn lại từ lập trình BASIC ở trường trung học), một cái gì đó cảm thấy sai về nó mặc dù. Tôi nghĩ rằng nó phá vỡ 3NF để có khoảng trống như thế này vì ý nghĩa của số bây giờ phụ thuộc vào giá trị của tất cả các mục khác. –

+1

@Greg Guida: Tôi không chắc chắn tôi làm theo. Ý nghĩa của số vẫn giống nhau. 1 nhỏ hơn 10 và 5 nhỏ hơn 10. – a1ex07

+1

Nguyên tắc tương tự hoạt động với phao hoặc đôi. –

2

Sử dụng Cách tiếp cận 1 và sống với hiệu suất tác động của các cập nhật chỉ mục. Trừ khi bạn đang xử lý hàng triệu mục mỗi trang, bạn sẽ không thấy hiệu suất thiếu và bạn giữ lại tất cả sức mạnh của SQL khi xử lý đặt dữ liệu.

Ngoài việc khó làm việc với SQL thuần túy phi thủ tục, cách tiếp cận sẽ vẫn yêu cầu bạn duyệt qua danh sách để tìm đúng nơi để kết nối lại "liên kết" khi sắp xếp lại mục.

+0

Về tuyên bố thứ hai của bạn, không cần phải duyệt toàn bộ danh sách để trao đổi hai mục. Tất cả những gì bạn cần là hai mục này, hai mục trỏ đến chúng và hai mục được chỉ bởi chúng. Tất cả những điều này có thể được chọn với các truy vấn liên kết rất đơn giản. (Xin lỗi vì đã đăng bài, tôi không biết đây có phải là thực hành không tốt ở đây ...) –

+0

@FabianPijcke Chắc chắn, nếu bạn có danh sách được liên kết kép. Tôi đã bình luận về "Phương pháp tiếp cận 2" của poster ban đầu dường như được liên kết đơn. Điều đó đang được nói, tôi có lẽ nên đề cập đến danh sách liên kết gấp đôi ... Có lẽ bạn có thể đăng câu trả lời đó? –

+0

@FabianPijcke Nhưng trong cả hai trường hợp, sắp xếp danh sách (ví dụ: nếu bạn muốn hiển thị danh sách theo thứ tự) sẽ không hiệu quả. –

-1

Thay thế cột chỉ mục bằng dấu thời gian (khi mục được đặt hàng), thứ tự theo dấu thời gian.

+1

thứ tự có thể là bất cứ điều gì người dùng muốn –

+0

Đủ công bằng - tôi nghĩ các mục từ giỏ hàng –

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