Storing item positions (for ordering) in a database efficiently

后端 未结 4 907
遇见更好的自我
遇见更好的自我 2021-02-01 17:40

Scenario:

There is a database of movies a user owns, movies are displayed on a page called \"my-movies\", the movies can be displayed in the order that the user desires.

4条回答
  •  遥遥无期
    2021-02-01 18:31

    If you use a combination of the position and a timestamp that the user put a movie in a given position rather than trying to maintain the actual position, then you can achieve a fairly simple means of both SELECTing and UPDATEing the data. For example; a base set of data:

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

    If you query the user's movies using a query like this:

    ;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
    

    Then you'll get the expected result:

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

    To move one of the rankings of the movies we need to update the position and the positionsetdatetime columns. For example, if userid 123 moves movie 95 from rank 5 to rank 2 then we do this:

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

    Which results in this (using the SELECT query above following the update):

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

    Then if userid 123 moves movie 96 to rank 1:

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

    We get:

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

    Of course you'll end up with duplicate position column values within the usermovies table, but with this method you'll never show that column, you simply use it along with positionsetdatetime to determine a sorted rank for each user and the rank you determine is the real position.

    If at some point you want the position column to properly reflect the movie rankings without reference to the positionsetdatetime you can use the movierank from the select query above to update the usermovies position column value, as it wouldn't actually affect the determined movie rankings.

提交回复
热议问题