在保持排序的同时将排序列表保存到数据库的最佳方法


70

我想知道是否有人能很好地解决我在过去几年中多次遇到的问题。

我有一个购物车,我的客户明确要求其订单量很大。因此,我需要将订单保留到数据库。

显而易见的方法是简单地插入一些OrderField,在其中我将数字0分配给N并以这种方式对其进行排序。

但是这样做会使重新订购变得更加困难,而且我不知何故觉得此解决方案有些脆弱,总有一天会再次出现在我身上。

(我将C#3,5与NHibernate和SQL Server 2005一起使用)

谢谢

Answers:


25

FWIW,我认为您建议的方式(即,将订单提交到数据库)并不是解决您的问题的好方法。我也认为这可能是最安全/最可靠的方法。


1
我以某种方式感到,保持秩序可能会在将来引起头痛。所以我问,也许有人找到更好的解决方案。
Tigraine

13
不用担心-没有比使用订购列更好的方法了。至于未来的麻烦……请不要针对您不了解的用例进行优化。
罗兰·特普

1
由于您通常会出于展示目的获取整个购物车,因此重新编号整个购物车实际上似乎不是问题
S.Lott

1
我同意,只是感觉太脆弱了。但是当我意识到我可以将整个排序逻辑放入存储库中之后,整个事情对于我的业务代码是透明的,它可以完美地工作。谢谢你的建议。
偏头痛

49

好的,这是我的解决方案,可以使与此线程相关的任何人都可以更轻松地进行编程。诀窍是能够在一次更新中更新插入/删除上方或下方的所有订单索引。

使用表中的数字(整数)列,SQL查询支持

CREATE TABLE myitems (Myitem TEXT, id INTEGER PRIMARY KEY, orderindex NUMERIC);

要删除订单索引为6的商品,请执行以下操作:

DELETE FROM myitems WHERE orderindex=6;    
UPDATE myitems SET orderindex = (orderindex - 1) WHERE orderindex > 6;

交换两个项目(4和7):

UPDATE myitems SET orderindex = 0 WHERE orderindex = 4;
UPDATE myitems SET orderindex = 4 WHERE orderindex = 7;
UPDATE myitems SET orderindex = 7 WHERE orderindex = 0;

即不使用0,因此将其用作虚拟对象以避免产生歧义。

要插入3:

 UPDATE myitems SET orderindex = (orderindex + 1) WHERE orderindex > 2;
 INSERT INTO myitems (Myitem,orderindex) values ("MytxtitemHere",3)

31

最好的解决方案是双链表。除索引外的所有操作都为O(1)。除了想要的项目上的where子句外,没有任何东西可以快速索引SQL。

0、10、20类型失败。序列列的那些失败。浮动顺序列在组移动时失败。

双链接列表是添加,删除,删除组,添加组,移动组的相同操作。单个链表也可以。我认为,使用SQL更好地使用双链接。单个链接列表要求您拥有整个列表。


3
这是恕我直言的最佳解决方案。例如,我的Netflix队列中有300多部电影。如果将项目从位置297移到顶部,netflix不必进行300次更新。最多只需进行3次更新
2012年

6
获得前50件商品的最佳方法是什么?我需要一一阅读,还是有任何魔术sql解决方案?
akn 2015年

@akn可以使用递归查询,然后将行数限制为50以获取前50行。
Decade Moon

2
我不了解双向链表如何帮助数据库中的“排序”和“将订单序列保存为float或int”?双向链表操作不是仅在应用程序端发生吗?如何使用列表更新数据库?您仍然必须编写一种算法来将有序列保存在数据库中。除非我缺少任何东西。
ANewGuyInTown

1
@ANewGuyInTown他说要实现行数据结构,就好像它是一个双链表一样。一个条目将引用列表层次结构中之前和之后的兄弟姐妹。
美国

10

如何使用链表实现?只有一列将保存下一项的值(订单号)。我认为这是迄今为止在两个之间插入订单时最容易使用的方法。无需重新编号。


5

不幸的是,对此没有万能的子弹。如果SELECT没有order by子句,则无法保证任何语句的顺序。您需要添加列并围绕它编程。

我不知道我会建议在订单顺序中添加空白,具体取决于列表的大小和网站上的点击数,您可能会从处理逻辑的开销中获得很少的收益(您仍然需要以满足所有空白都已用尽的情况)。我会仔细看一看,这会给您带来什么好处。

对不起,我无法提供更好的服务,希望对您有所帮助。


2
由于通常出于显示目的而获取整个购物车,因此对整个购物车重新编号似乎实际上不是问题。
S.Lott,

1
如果列表恰好足够大,以至于您在对某些东西进行重新排序时不希望更新每一行,则可以使用浮点DisplayOrder列。我没有尝试过,但这只是一个主意...
Kevin Coulombe 2013年

4

我完全不推荐A,AA,B,BA,BB方法。确定层次结构并在它们之间插入条目涉及很多额外的处理,这一点都不有趣。

只需添加一个OrderField,整数。不要使用空格,因为这样一来,您必须在下一个中间插入片段上使用非标准的“步骤”,否则您将必须先重新同步列表,然后添加一个新条目。

拥有0 ... N很容易重新排序,如果您可以在SQL外部使用Array方法或List方法对整个集合进行重新排序,然后更新每个条目,或者可以弄清楚要插入的位置,并相应地在每个条目之前或之后+1或-1。

一旦为它编写了一个小库,这将是小菜一碟。


1

我只想插入一个订单字段。这是最简单的方法。如果客户可以重新订购字段,或者您需要在中间插入字段,则只需重写该批次中所有项目的订购字段即可。

如果由于限制了插入和更新的性能而发现此限制,则可以使用varchar字段而不是整数。这样可以在插入时实现很高的精度。例如,要在项目“ A”和“ B”之间插入,您可以插入订购为“ AA”的项目。但是,对于购物车来说,这几乎可以肯定是过头了。


之后是什么Z在你的varchar字段排序?
cdmckay

1

在cartItem之上的抽象级别上,假设CartOrder(CartItem的值为1-n),您可以维护一个名为itemOrder的字段,该字段可以是逗号分隔的cartItem记录的相关ID(PK)列表。您将需要在应用程序层进行解析并相应地安排项目模型。这种方法的最大优点是可以进行订单改组,单个对象可能没有更改,但是由于订单作为订单项表行内的索引字段而持久存在,因此您必须为每个订单项发出更新命令行更新其索引字段。请让我知道您对这种方法的批评,我很想知道这种方法可能会失败。


1

我这样务实地解决了这个问题:

  1. 顺序在用户界面中定义。

  2. 后端获取一个POST请求,该请求包含ID和列表中每个项目的对应位置。

  3. 我开始交易并更新每个ID的头寸。

做完了

因此订购很昂贵,但阅读订购清单却非常便宜。


0

我建议您在订单号中留出空隙,因此,代替1,2,3等,而使用10,20,30 ...如果您只需要再插入一件商品,则可以将其放在15,而不是重新排列所有商品在那时候。


22
那只有在您用BASIC编写系统的情况下才可以;-)
belugabob

10
严重的是,这种方法只会推迟进行实际排序的需要,然后您的代码中会有两个不同的功能。如果您可能必须在某个时候处理某种排序,为什么不只是在一开始就这样做并保持较低的复杂性呢?
belugabob

0

好吧,我想说的简短答案是:

在cartcontents表中创建自动标识的主键,然后以正确的自上而下的顺序插入行。然后,通过从表中按主键自动标识列的顺序进行选择,可以得到相同的列表。这样,您必须删除所有物品,然后重新插入,以防购物车内容发生变化。(但这仍然是一种很干净的方法)。如果这不可行,那么请像其他人建议的那样使用order列。


-1

当我使用Hibernate并且需要保存a的顺序时@OneToMany,我使用aMap而不是a List

@OneToMany(fetch = FetchType.EAGER, mappedBy = "rule", cascade = CascadeType.ALL)
@MapKey(name = "position")
@OrderBy("position")
private Map<Integer, RuleAction>    actions             = LazyMap.decorate(new LinkedHashMap<>(), FactoryUtils.instantiateFactory(RuleAction.class, new Class[] { Rule.class }, new Object[] { this }));

在此Java示例中,position是的Integer属性,RuleAction因此顺序以这种方式保留。我猜在C#中,这看起来很相似。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.