2012-06-19 28 views
15

Kịch bản:lưu trữ vị trí mục (đối với trật tự) trong một cơ sở dữ liệu một cách hiệu quả

Có một cơ sở dữ liệu của bộ phim một người dùng sở hữu, phim được hiển thị trên một trang gọi là "my-phim", những bộ phim có thể được hiển thị trong thứ tự mà người dùng mong muốn. Ví dụ: "Fight Club" ở vị trí số 1, "Drive" ở vị trí số 3 và cứ tiếp tục như vậy.

Các giải pháp rõ ràng là để lưu trữ một vị trí với mỗi mục, ví dụ:

movieid, userid, vị trí
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3

Sau đó, khi xuất dữ liệu được sắp xếp theo vị trí. Phương pháp này hoạt động tốt cho đầu ra tuy nhiên nó có một vấn đề khi cập nhật: vị trí của một mục tất cả các vị trí khác cần phải được cập nhật vì vị trí là tương đối. Nếu phim # 3 hiện đang ở vị trí số 2 thì phim # 3 bây giờ cần phải được cập nhật lên vị trí số 2. Nếu cơ sở dữ liệu chứa 10.000 phim và phim được chuyển từ vị trí số 1 sang vị trí # 9999, đó là gần 10.000 hàng sẽ được cập nhật!

Giải pháp duy nhất của tôi là lưu trữ vị trí riêng biệt, thay vì có một trường riêng cho từng vị trí mục, nó chỉ là một kết xuất dữ liệu lớn của các vị trí được thực hiện trong thời gian chạy và liên kết với từng mục (json, xml, bất kỳ) cảm thấy ... không hiệu quả vì cơ sở dữ liệu không thể để lại để phân loại.

Câu hỏi tóm tắt của tôi: Cách hiệu quả nhất để lưu trữ các vị trí mục trong danh sách thân thiện với việc tìm nạp và cập nhật?

Trả lời

1

Lưu trữ kiểu danh sách liên kết đơn đặt hàng. Thay vì lưu vị trí tuyệt đối, hãy lưu ID của mục trước đó. Bằng cách đó, mọi thay đổi chỉ yêu cầu bạn cập nhật hai hàng.

movieid | userid | previousid 
    1 | 1 | 
    2 | 1 | 1 
    3 | 1 | 4 
    4 | 1 | 2 

Để có được những bộ phim theo thứ tự ...

SELECT movieid WHERE userid = 1 ORDER BY previousid 

-> 1, 2, 4, 3 

Để (nói) di chuyển # 4 lên một không gian:

DECLARE @previousid int, @currentid int 
SET @previousid = SELECT previousid FROM movies WHERE movieid = @currentid 

-- current movie's previous becomes its preceding's preceding 
UPDATE movies SET previousid = 
    (SELECT previousid FROM movies WHERE movieid = @previousid) 
WHERE movieid = @currentid 

-- the preceding movie's previous becomes the current one's previous 
UPDATE movies SET previousid = @currentid WHERE movieid = @previousid 

Đó là vẫn còn 1 đọc + 2 viết, nhưng nó đánh bại 10.000 bài viết.

+0

và truy vấn SQL sẽ liệt kê phim là gì? – bjan

+0

@bjan Lựa chọn nên khá đơn giản ... cập nhật là một mẹo nhỏ hơn, nhưng tôi nghĩ rằng nó hoạt động. – McGarnagle

+0

Theo thử nghiệm của tôi, kết quả là trùng lặp trước đó !! – bjan

6

Tôi đã đấu tranh với những gì tốt nhất để làm với tình huống này và đã nhận ra rằng BY FAR giải pháp tốt nhất là danh sách/mảng của phim theo thứ tự bạn muốn, ví dụ;

userId, moviesOrder

1: [4,3,9,1 ...]

rõ ràng bạn sẽ serialise mảng của bạn.

'cảm thấy ... không hiệu quả'?

xem xét người dùng có danh sách 100 phim. Tìm kiếm theo vị trí sẽ là một truy vấn cơ sở dữ liệu, một chuỗi để chuyển đổi mảng và sau đó moviesOrder [index]. Có thể chậm hơn so với một tra cứu DB thẳng nhưng vẫn rất rất nhanh.

OTOH, hãy cân nhắc xem bạn có thay đổi thứ tự hay không;

với vị trí được lưu trữ trong db bạn cần tối đa 100 thay đổi hàng, so với mối nối mảng. Ý tưởng danh sách được liên kết là thú vị nhưng không hoạt động như được trình bày, sẽ phá vỡ mọi thứ nếu một phần tử đơn lẻ không thành công và trông chậm hơn rất nhiều. Các ý tưởng khác như để lại những khoảng trống, sử dụng float là hoàn toàn khả thi mặc dù một mớ hỗn độn, và dễ bị thất bại tại một số điểm trừ khi bạn GC.

Dường như có cách nào tốt hơn để làm điều đó trong SQL, nhưng thực sự thì không.

8

Nếu bạn sử dụng kết hợp vị trí và dấu thời gian mà người dùng đặt phim ở vị trí nhất định thay vì cố gắng duy trì vị trí thực, thì bạn có thể đạt được một phương tiện khá đơn giản gồm cả CHỌN và CẬP NHẬT dữ liệu. Ví dụ; một bộ cơ sở dữ liệu:

create table usermovies (userid int, movieid int, position int, positionsetdatetime datetime) 

insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 99, 1, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 98, 2, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 97, 3, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 96, 4, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 95, 5, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (123, 94, 6, getutcdate()) 

insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 99, 1, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 98, 2, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 97, 3, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 96, 4, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 95, 5, getutcdate()) 
insert into usermovies (userid, movieid, position, positionsetdatetime) 
values (987, 94, 6, getutcdate()) 

Nếu bạn truy vấn phim của người dùng sử dụng một truy vấn như thế này:

;with usermovieswithrank as (
    select userid 
    , movieid 
    , dense_rank() over (partition by userid order by position asc, positionsetdatetime desc) as movierank 
    from usermovies 
) 
select * from usermovieswithrank where userid=123 order by userid, movierank asc 

Sau đó, bạn sẽ nhận được kết quả mong đợi:

USERID MOVIEID  MOVIERANK 
123  99   1 
123  98   2 
123  97   3 
123  96   4 
123  95   5 
123  94   6 

Để di chuyển một trong những thứ hạng của phim chúng ta cần cập nhật vị trí và cột positionsetdatetime. Ví dụ, nếu userid 123 di chuyển phim 95 từ vị trí thứ 5 để xếp hạng 2 thì chúng ta làm điều này:

update usermovies set position=2, positionsetdatetime=getutcdate() 
where userid=123 and movieid=95 

Những kết quả trong việc này (bằng cách sử dụng truy vấn SELECT ở trên sau khi cập nhật):

USERID MOVIEID  MOVIERANK 
123  99   1 
123  95   2 
123  98   3 
123  97   4 
123  96   5 
123  94   6 

Sau đó, nếu userid 123 di chuyển phim 96 để xếp hạng 1:

update usermovies set position=1, positionsetdatetime=getutcdate() 
where userid=123 and movieid=96 

Chúng tôi nhận được:

USERID MOVIEID  MOVIERANK 
123  96   1 
123  99   2 
123  95   3 
123  98   4 
123  97   5 
123  94   6 

Tất nhiên bạn sẽ kết thúc với các giá trị cột vị trí trùng lặp trong bảng usermovies, nhưng với phương pháp này bạn sẽ không bao giờ hiển thị cột đó, bạn chỉ cần sử dụng nó cùng với positionsetdatetime để xác định thứ hạng được sắp xếp cho từng người dùng và thứ hạng bạn xác định là vị trí thực sự.

Nếu tại một thời điểm nào đó bạn muốn cột vị trí phản ánh đúng thứ hạng phim mà không tham chiếu đến vị tríđịnh giờ bạn có thể sử dụng movierank từ truy vấn chọn ở trên để cập nhật giá trị cột vị trí usermovies, vì nó sẽ không thực sự ảnh hưởng đến xếp hạng phim được xác định.

+2

Chỉ cần nhận thấy câu hỏi này là một năm cũ - oops! Đừng bận tâm, có lẽ gợi ý của tôi sẽ giúp ai đó :-) – Elliveny

+0

Điều này không có tác dụng nếu người dùng xem phim _down_ trong danh sách.Ví dụ: nếu họ di chuyển phim 98 từ vị trí 4 đến vị trí 6, sẽ có hai phim có vị trí 6, nhưng phim 98 sẽ được hiển thị đầu tiên (ở vị trí 5) do vị trí đặt gần đây nhất của nó. – bergie3000

+0

@ bergie3000 Bạn đúng - Tôi xin lỗi tôi đã bỏ lỡ điều đó! Tôi nghi ngờ nó có thể được khắc phục dễ dàng bằng cách thêm 1 vào vị trí mong muốn khi di chuyển xuống; vì vậy trong ví dụ của bạn thiết lập phim 98 từ vị trí 4 đến vị trí 7 (tức là vị trí mong muốn 6, cộng 1) sẽ làm điều đó, tôi nghĩ vậy? – Elliveny

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