我想知道是否有人对我在过去几年中多次遇到的问题有很好的解决方案。
我有一个购物车,我的客户明确要求它的订单很重要。所以我需要将订单保存到数据库。
显而易见的方法是简单地插入一些 OrderField,我将在其中将数字 0 分配给 N 并以这种方式对其进行排序。
但是这样做会使重新排序变得更加困难,而且我不知何故觉得这个解决方案有点脆弱,总有一天会回来找我。
(我将 C# 3,5 与 NHibernate 和 SQL Server 2005 一起使用)
谢谢
我想知道是否有人对我在过去几年中多次遇到的问题有很好的解决方案。
我有一个购物车,我的客户明确要求它的订单很重要。所以我需要将订单保存到数据库。
显而易见的方法是简单地插入一些 OrderField,我将在其中将数字 0 分配给 N 并以这种方式对其进行排序。
但是这样做会使重新排序变得更加困难,而且我不知何故觉得这个解决方案有点脆弱,总有一天会回来找我。
(我将 C# 3,5 与 NHibernate 和 SQL Server 2005 一起使用)
谢谢
好的,这是我的解决方案,可以让任何发生在这个线程上的人都更容易编程。诀窍是能够在一次更新中更新插入/删除上方或下方的所有订单索引。
在 SQL 查询支持的表中使用数字(整数)列
CREATE TABLE myitems (Myitem TEXT, id INTEGER PRIMARY KEY, orderindex NUMERIC);
要删除 orderindex 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;
ie 0 没有被使用,所以使用它作为一个虚拟对象以避免有一个模棱两可的项目。
在 3 处插入:
UPDATE myitems SET orderindex = (orderindex + 1) WHERE orderindex > 2;
INSERT INTO myitems (Myitem,orderindex) values ("MytxtitemHere",3)
最好的解决方案是双向链接列表。O(1) 对于除索引之外的所有操作。除了您想要的项目上的 where 子句之外,没有什么可以快速索引 SQL。
0,10,20 种类型失败。序列列失败。浮动序列列在组移动时失败。
双向链表对于添加、删除、组删除、组添加、组移动是相同的操作。单链表也可以。在我看来,双链接使用 SQL 更好。单链表要求您拥有整个列表。
FWIW,我认为您建议的方式(即将订单提交到数据库)对于您的问题来说并不是一个糟糕的解决方案。我也认为这可能是最安全/最可靠的方式。
使用链表实现怎么样?有一列将保存下一个项目的值(订单号)。我认为这是迄今为止在两者之间插入订单时最容易使用的。无需重新编号。
不幸的是,没有灵丹妙药。如果没有 order by 子句,您无法保证任何SELECT
语句的顺序。您需要添加列并围绕它进行编程。
我不知道我是否建议在订单序列中添加间隙,具体取决于列表的大小和网站上的点击量,处理逻辑的开销可能会很少(你仍然需要以适应所有间隙都用完的场合)。我会仔细看看这会给你带来什么好处。
对不起,我不能提供更好的东西,希望这有帮助。
我根本不会推荐 A、AA、B、BA、BB 方法。确定层次结构涉及很多额外的处理,并且在两者之间插入条目一点也不好玩。
只需添加一个 OrderField,整数。不要使用间隙,因为您必须在下一个中间插入时使用非标准“步骤”,或者您必须先重新同步您的列表,然后添加一个新条目。
有 0...N 很容易重新排序,如果您可以使用 SQL 之外的 Array 方法或 List 方法将集合重新排序为一个整体,然后更新每个条目,或者您可以找出插入的位置,并且相应地在其之后或之前的每个条目+1或-1。
一旦你为它编写了一个小库,它就会变得轻而易举。
我只想插入一个订单字段。它是最简单的方法。如果客户可以重新排序字段或者您需要在中间插入,那么只需重写该批次中所有项目的订单字段。
如果您发现由于插入和更新性能不佳而导致此限制,则可以使用 varchar 字段而不是整数。这允许在插入时具有相当高的精度。例如,要在项目“A”和“B”之间插入,您可以插入订购为“AA”的项目。不过,这对于购物车来说几乎可以肯定是矫枉过正。
在购物车项目之上的抽象级别上,假设 CartOrder(与 CartItem 有 1-n),您可以维护一个名为 itemOrder 的字段,它可能只是一个逗号分隔的 cartItem 记录相关的 id(PK) 列表。它将在应用程序层,您需要对其进行解析并相应地安排您的项目模型。这种方法的最大优点是在订单重新洗牌的情况下,可能不会对单个对象进行更改,但是由于订单作为索引字段保留在订单项目表行中,因此您必须为每个对象发出更新命令行更新其索引字段。请让我知道您对这种方法的批评,我很想知道这可能会以何种方式失败。
我像这样务实地解决了它:
订单在 UI 中定义。
后端收到一个 POST 请求,其中包含列表中每个项目的 ID 和相应的位置。
我开始交易并更新每个 ID 的位置。
完毕。
所以订购是昂贵的,但阅读订购的列表是超级便宜的。
我建议在订单号中保留空白,因此不要使用 1、2、3 等,而是使用 10、20、30 ...在那时候。
好吧,我想说的简短答案是:
在 cartcontents 表中创建一个 autoidentity 的主键,然后以正确的自上而下的顺序插入行。然后通过按主键自动标识列的顺序从表中选择,将为您提供相同的列表。通过这样做,您必须删除所有项目并重新插入,以防购物车内容发生变化。(但这仍然是一种非常干净的方法)如果这不可行,那么就像其他人建议的那样使用订单列。
当我使用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# 中这看起来很相似。