2011-12-22 67 views
26

比方说,我在购物网站的数据库中有一个Product表,用于保存商店产品的描述,价格等。使客户能够重新订购这些产品的最有效方式是什么?使用数据库表中的排序顺序列

我创建了一个Order列(整数)用于排序记录,但由于原始方法我用来更改每个记录的顺序,而实际上需要更改之后,这给我带来了一些性能问题。举个例子:

Id Order 
5  3 
8  1 
26 2 
32 5 
120 4 

现在我能做些什么来改变与ID=26至3记录的顺序?

我所做的是创建一个过程,检查目标订单(3)中是否有记录,如果不是,则更新行的订单(ID = 26)。如果目标订单中有记录,则过程执行自身发送该行的ID为target order + 1作为参数。

这导致更新一个我想改变,以腾出空间后,每一个记录:

Id Order 
5  4 
8  1 
26 3 
32 6 
120 5 

那么,什么会更聪明的人吗?

  • 我使用SQL Server 2008 R2。

编辑:

我需要的项目的顺序列与不涉及辅助键分拣就足够了。单独订单栏必须为其记录指定一个独特的地方。

除了所有,我不知道我是否可以实现像链接列表:'下一个'列而不是'订单'列以保留下一个项目ID。但我不知道如何编写以正确顺序检索记录的查询。如果有人对这种方法有一个想法,请分享。

+12

稍有经验的人不会把一列'Order'因为这是已经在SQL保留关键字.... :-) – 2011-12-22 18:20:15

回答

24
Update product set order = order+1 where order >= @value changed 

虽然随着时间的推移,你会得到您的订单越来越大的“空间”,但它还是会“之类的”

这将增加1到值被改变和一个后它的每一个值声明,但上述声明仍然如此。您的订单中可能会形成越来越大的“空格”,可能会超过INT值。

替代溶液中对没有空格的愿望:

想象为一个过程:UpdateSortOrder具有取决于如果新的/旧的顺序向上移动或@NewOrderVal,@ IDToChange,@ OriginalOrderVal

两步骤过程的参数下来。

If @NewOrderVal < @OriginalOrderVal --Moving down chain 

--Create space for the movement; no point in changing the original 
    Update product set order = order+1 
    where order BETWEEN @NewOrderVal and @OriginalOrderVal-1; 

end if 

If @NewOrderVal > @OriginalOrderVal --Moving up chain 

--Create space for the momvement; no point in changing the original 
    Update product set order = order-1 
    where order between @OriginalOrderVal+1 and @NewOrderVal 
end if 

--Finally update the one we moved to correct value 

    update product set order = @newOrderVal where [email protected]; 

关于最佳实践;大多数我一直在使用的环境都希望按类别进行分组,并按字母顺序排序或基于“销售受欢迎程度”进行排序,从而无需提供用户定义的排序。

+1

谢谢。尽管它会起作用并不完美,并且这是开发中最重要的事情。但是我期待着这种情况下的“最佳实践”。我经常需要它,每次都想出不同的方法。我很惊讶没有人人都知道和使用的众所周知的方法。 – 2011-12-24 10:14:59

+0

好吧,你可以让用户点击一个“完成”按钮,然后使用设置“订单”字段= rownum的查询排序按“订单” – xQbert 2011-12-24 17:04:03

+0

确实是一个很好的解决方案。谢谢。但我还有一个问题:在更新第二个示例中需要更改的记录之前,您更新了其中的记录。这意味着一段时间内将有两行共享相同的订单值。我如何能够做到这一点,如果我对订单列有一个唯一的约束? – 2012-01-06 10:41:08

4

我过去曾经使用过的一个解决方案,取得了一些成功,就是使用“重量”而不是“顺序”。重量是显而易见的,一个物品越重(即:数字越低)沉入底部,打火机(数量越高)越高。

如果我有多个重量相同的项目,我认为它们具有相同的重要性,我按字母顺序排列。

这意味着你的SQL会是这个样子:

ORDER BY 'weight', 'itemName' 

希望帮助。

+0

谢谢。这是一个解决方案,但它与我目前的方法没有什么不同。这是我的错,因为我应该指定我需要订单栏是唯一的。 (我会马上更新这个问题)我需要客户能够精确地选择哪个项目最先。我没有像Name这样的第二个条件,在这种情况下Order必须足够。如果我有两个权重为10和11的项目,我如何在不改变权重的情况下在它们之间出现记录(因此后面的权重是我的实际问题)。 – 2011-12-24 10:04:18

+0

这个订单是否必须是独一无二的?如果是这样,为什么?概述的技术完全可以在列上有一个唯一的约束,所以顺序是唯一的。 – 2011-12-24 17:02:46

+0

对不起,我不明白。差距不是问题。我只是不明白当两件物品具有相同的重量时会发生什么情况,或者我怎样才能使记录出现在两个加权为10和11的记录之间(如我之前的评论中提到的那样)。特别是如果我对列有一个唯一的约束。在这种情况下,我必须改变其他项目的重量之一。你是否建议我不应该增加/减少1?我道歉,如果我在这里失去了一些明显的东西。 – 2012-01-06 10:07:55

5

使用旧把戏,用BASIC程序(在其他地方):10或其他方便的增量跳号的顺序列。然后,您可以在两个现有数字(相距10)之间插入一行(实际上,如果幸运的话,最多可以有9行)。或者,您可以将行370移动到565,而不必从570向上更改任何行。

1

(对不起,我正在学习)

很简单。你需要有你需要有2列

1)PK = INT 32位

2)为了= 64 BIGINT(BIGINT,不是双重 “基数洞”

结构!!!)

插入/更新

1)当您插入您必须设置顺序第一的新纪录= ROUND(max_bigint/2)。

2)如果在必须设置顺序表中的开头插入=圆形(“第一个记录的顺序”/2)

3)如果在表中的端部插入必须设置顺序= ROUND( “max_bigint - 为了最后一条记录的”/2)

4)如果你在中间插入必须设置顺序= ROUND( -/2)

这种方法“之前的订单记录后记录的顺序”有很大的怀抱。如果你有约束错误,或者你认为你有小基数,你可以重建顺序列(normalize)。

与归一化极大性情况(具有这种结构),则可以在32位具有“基数孔”。

很简单,快速!

记住没有双重!只有INT - 顺序是精确值!

+0

@AndyM:这不是一个新问题,它是一个试图回答给定的问题 – cfi 2014-08-21 18:39:12

+0

爱,我误读了,我的坏! – 2014-08-21 18:43:55

+3

但是,在插入64行后,您将用完“订单”的值(订单值将小于1) – 2015-03-30 11:58:01

1

我目前正在开发一个树结构,需要订购的数据库。我使用一种链接列表方法,将在客户端(而不是数据库)上进行排序。排序也可以通过递归查询在数据库中完成,但这对于此项目不是必需的。

我做了这个文档描述我们如何去实现排序顺序的存储,包括在PostgreSQL的一个例子。请随意发表评论!

https://docs.google.com/document/d/14WuVyGk6ffYyrTzuypY38aIXZIs8H-HbA81st-syFFI/edit?usp=sharing

+0

+1,很好的示例。我有两个问题:**第一:**为什么你有'父'和'先导' - 为什么双链表而不是链表? **第二:**您如何选择订购的记录:例如,按其父母/子女关系的顺序选择前100个记录。 – 2014-10-14 13:50:56

+0

父指向树结构中的父节点。前面的列定义了节点的顺序。我最近使用SELECT更新了文档中的示例,通过递归公用表表达式以正确的顺序获取节点。 – Elmer 2014-10-17 04:00:50

2

下面是一个使用公共表表达式(CTE)的替代方法。

此方法尊重SortOrder列上的唯一索引,并将关闭排序顺序中可能从早期的DELETE操作留下的任何间隙。

/* For example, move Product with id = 26 into position 3 */ 
DECLARE @id int = 26 
DECLARE @sortOrder int = 3 


;WITH Sorted AS (
    SELECT Id, 
      ROW_NUMBER() OVER (ORDER BY SortOrder) AS RowNumber 
    FROM Product 
    WHERE Id <> @id 
) 

UPDATE p 
SET  p.SortOrder = 
     (CASE 
      WHEN p.Id = @id THEN @sortOrder 
      WHEN s.RowNumber >= @sortOrder THEN s.RowNumber + 1 
      ELSE s.RowNumber 
     END) 
FROM Product p 
     LEFT JOIN Sorted s ON p.Id = s.Id