Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在数据库中高效地存储项目位置(用于订购)_Sql_Database_Theory - Fatal编程技术网

Sql 在数据库中高效地存储项目位置(用于订购)

Sql 在数据库中高效地存储项目位置(用于订购),sql,database,theory,Sql,Database,Theory,情景: 有一个用户拥有的电影数据库,电影显示在一个名为“我的电影”的页面上,电影可以按照用户希望的顺序显示。例如,“搏击俱乐部”在位置1,“驾驶”在位置3等等 显而易见的解决方案是为每个项目存储一个位置,例如: 电影ID、用户ID、位置 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 然后在输出数据时按位置排序。这种方法对于输出效果很好,但是在更新时会出现问题:一个项目的位置和所有其他位置都需要更新,因为位置是相对的。如果电影3现在位于位置2,那么电影3现在需要更新到位置2。如果数据

情景:

有一个用户拥有的电影数据库,电影显示在一个名为“我的电影”的页面上,电影可以按照用户希望的顺序显示。例如,“搏击俱乐部”在位置1,“驾驶”在位置3等等

显而易见的解决方案是为每个项目存储一个位置,例如:

电影ID、用户ID、位置
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3

然后在输出数据时按位置排序。这种方法对于输出效果很好,但是在更新时会出现问题:一个项目的位置和所有其他位置都需要更新,因为位置是相对的。如果电影3现在位于位置2,那么电影3现在需要更新到位置2。如果数据库包含10000部电影,并且一部电影从位置#1移动到位置#9999,则需要更新的行数几乎为10000行

我唯一的解决方案是单独存储定位,而不是为每个项目位置单独设置一个字段。它只是一个大数据转储位置,在运行时执行,并与每个项目(json、xml等)关联,但感觉。。。效率低下,因为不能让数据库进行排序


我的总结问题:在易于获取和更新的列表中存储项目位置的最有效方法是什么?

存储订单链接列表样式。保存上一项的ID,而不是保存绝对位置。这样,任何更改只需要更新两行

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向上移动一个空间:

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写,但它胜过10000写。

我一直在努力解决这个问题,并意识到到到目前为止,最好的解决方案是按照您想要的顺序列出电影列表/阵列,例如

用户ID,moviesOrder

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

显然,您将序列化阵列

“那感觉。。。低效的

假设用户有100部电影的列表。按位置搜索将是一个数据库查询、字符串到数组的转换,然后是moviesOrder[索引]。可能比直接的DB查找慢,但仍然非常快

如果您更改订单,请考虑:p> 对于存储在db中的位置,与阵列拼接相比,最多需要更改100行。链表的想法很有趣,但并不像上面所说的那样有效,如果一个元素失败,它会破坏一切,而且看起来也慢得多。其他的想法,比如留下空白,使用float,是可行的,尽管很混乱,并且在某个时候很容易失败,除非你使用GC


在SQL中似乎应该有更好的方法来实现这一点,但实际上没有。

如果结合使用位置和时间戳,用户将电影放在给定位置,而不是试图保持实际位置,那么就可以实现选择和更新数据的相当简单的方法。例如一组基本数据:

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
要移动其中一个电影排名,我们需要更新position和positionsetdatetime列。例如,如果userid 123将电影95从排名5移动到排名2,那么我们将执行以下操作:

update usermovies set position=2, positionsetdatetime=getutcdate() 
where userid=123 and movieid=95 
其结果是(在更新后使用上面的SELECT查询):

然后,如果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一起使用,来确定每个用户的排序列,并且您确定的列是真实的位置

如果在某个时刻,您希望position列在不参考positionsetdatetime的情况下正确反映电影排名,您可以使用上面select查询中的movierank来更新usermovies position列值,因为它实际上不会影响确定的电影排名

ID   NAME  POSITION
7     A       1
9     B       2
13    C       3
15    D       4
21    F       5
给定当前场景,如果我们要将项目D移动到位置2,我们可以搜索2(我们要移动项目的位置)和4(项目的当前位置)之间的间隔,并编写一个查询,将+1添加到该间隔内每个元素的位置,因此在这种情况下,我们可以执行以下步骤:

  • 搜索位置>=2且位置<4的间隔中的项目,并将+1添加到其位置
  • 将项目D位置设置为2
  • 这将产生以下信息: A->1,B->3,C->4,D->2,F->5

    如果我们想把B移到D,那么我们需要做相反的事情,用a-1代替

  • 在位置>2和位置0的间隔内搜索项目; 更新电影集位置=18,其中id=130; --上移 更新电影集位置=位置+1,其中位置<18且位置>=13且id>0; 更新电影集位置=13,其中id=130;
    什么是列出电影的SQL查询?@bjan选择应该相当简单。。。更新有点麻烦,但我认为这是可行的。根据我的测试,这会导致重复的以前的ID!!那个让电影井然有序的查询不太管用。考虑:(id,prev):(1,2),(2,3),(3,u)。该查询将返回3、1、2。应该是3,2,1。在给定模式的纯SQL中,似乎没有很好的(非递归、一次扫描)查询方法。如果你改为选择movieid,userid=1的previousid,那么用任何其他编程语言对它们进行分类都是很简单的。@McGarnagle更新看起来很简单,但是没有简单的选择方法。注意到这个问题已经有一年了-哎呀!没关系,也许我的建议对某些人有帮助:-)但是如果用户从列表中下载了一部电影,这就行不通了。例如,如果他们从位置4移动电影98
    ID   NAME  POSITION
    7     A       1
    9     B       2
    13    C       3
    15    D       4
    21    F       5
    
    -- MOVE DOWN
    UPDATE movie SET position = position-1  WHERE position <= 18 AND position > 13 AND id > 0;
    UPDATE movie SET position = 18 WHERE id = 130;
    
    -- MOVE UP
    UPDATE movie SET position = position+1  WHERE position < 18 AND position >= 13 AND id > 0;
    UPDATE movie SET position = 13 WHERE id = 130;