25

シナリオ:

ユーザーが所有する動画のデータベースがあり、動画は「my-movies」というページに表示され、ユーザーが望む順序で動画を表示できます。たとえば、「ファイト クラブ」が 1 位、「ドライブ」が 3 位、というように続きます。

明らかな解決策は、各アイテムの位置を保存することです。次に例を示します。

ムービー ID、ユーザー ID、位置
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3

次に、データを出力するときに、位置によって並べ替えられます。このメソッドは出力には問題なく機能しますが、更新時に問題があります。位置は相対的であるため、アイテムの位置、その他すべての位置を更新する必要があります。ムービー #3 が位置番号 2 にある場合、ムービー #3 を位置 #2 に更新する必要があります。データベースに 10,000 本の映画が含まれていて、映画が位置 #1 から位置 #9999 に移動された場合、更新されるのは約 10,000 行になります!

私の唯一の解決策は、位置を個別に保存することです。各アイテムの位置に個別のフィールドを用意するのではなく、実行時に取得され、各アイテム (json、xml など) に関連付けられた位置の 1 つの大きなデータ ダンプですが、それは感じます.. . データベースにソートを任せることができないため、非効率的です。

私の要約した質問:フェッチと更新に適したリストにアイテムの位置を保存する最も効率的な方法は何ですか?

4

5 に答える 5

17

実際の位置を維持しようとするのではなく、ユーザーがムービーを特定の位置に配置した位置とタイムスタンプの組み合わせを使用すると、データの選択と更新の両方を行うかなり単純な手段を実現できます。例えば; データの基本セット:

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())

次のようなクエリを使用してユーザーの映画をクエリするとします。

;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

次に、期待される結果が得られます。

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

映画のランキングの 1 つを移動するには、position 列と positionsetdatetime 列を更新する必要があります。たとえば、userid 123 が映画 95 をランク 5 からランク 2 に移動する場合、次のようにします。

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

結果は次のようになります (更新後に上記の SELECT クエリを使用):

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

次に、userid 123 が映画 96 をランク 1 に移動した場合:

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

我々が得る:

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

もちろん、usermovies テーブル内で position 列の値が重複することになりますが、この方法ではその列を表示することはありません。positionsetdatetime と一緒に使用して、各ユーザーの並べ替えられたランクを決定し、決定したランクは次のとおりです。本当の位置。

ある時点で、positionsetdatetime を参照せずに位置列に映画のランキングを適切に反映させたい場合は、上記の選択クエリの movierank を使用して、決定された映画のランキングに実際には影響しないため、usermovies の位置列の値を更新できます。

于 2014-06-13T15:45:08.193 に答える
11

私はこの状況をどうするのが最善かについて苦労してきましたが、最良の解決策は映画のリスト/配列をあなたが望む順序で並べることであることに気づきました。

userId、moviesOrder

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

明らかに、配列をシリアル化します。

「それは…効率が悪い」?

ユーザーが 100 本の映画のリストを持っていたとします。位置による検索は、1 つのデータベース クエリ、文字列から配列への変換、そして movieOrder[index] になります。単純な DB ルックアップよりも遅い可能性がありますが、それでも非常に高速です。

OTOH、順序を変更するかどうかを検討してください。

配列スプライスと比較して、データベースに格納された位置では、最大 100 行の変更が必要です。リンクされたリストのアイデアは興味深いですが、提示されたとおりには機能せず、1 つの要素が失敗するとすべてが壊れてしまい、非常に遅く見えます。ギャップを残す、フロートを使用するなどの他のアイデアは、面倒ではありますが実行可能であり、GC を使用しない限り、ある時点で失敗する傾向があります。

SQL でこれを行うためのより良い方法があるはずですが、実際にはありません。

于 2012-10-11T21:44:39.043 に答える
4

順序リンク リスト スタイルを保存します。絶対位置を保存する代わりに、前のアイテムの ID を保存します。そうすれば、どのような変更でも、2 つの行を更新するだけで済みます。

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

映画を順番に取得するには...

SELECT movieid WHERE userid = 1 ORDER BY previousid

-> 1, 2, 4, 3

(たとえば) #4 を 1 つ上に移動するには:

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

それでも読み取り 1 回 + 書き込み 2 回ですが、書き込み 10,000 回を上回っています。

于 2012-06-19T04:30:47.113 に答える