使用数据库表中的排序顺序列

时间:2011-12-22 17:58:44

标签: sql-server database sorting

假设我在购物网站的数据库中有一个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。但我不知道如何编写以正确顺序检索记录的查询。如果有人也对这种方法有所了解,请分享。

6 个答案:

答案 0 :(得分:26)

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

虽然随着时间的推移,您的订单中会有越来越大的“空格”,但它仍会“排序”

这将在一个语句中将要更改的值和其后的每个值加1,但上述语句仍然为true。将在您的订单中形成越来越大的“空格”,可能达到超过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 ID=@IDToChange;

关于最佳做法;我所经历的大多数环境通常都需要按类别分组并按字母顺序或基于“销售中的受欢迎程度”排序的内容,从而无需提供用户定义的排序。

答案 1 :(得分:5)

使用BASIC程序(以及其他地方)使用的旧技巧:将订单列中的数字跳过10或其他一些方便的增量。然后,您可以在两个现有数字(相隔10个)之间插入一行(实际上,最多9行,如果您很幸运)。或者,您可以将行370移动到565,而无需从570向上更改任何行。

答案 2 :(得分:4)

我过去使用的一个解决方案是使用“权重”而不是“顺序”。重量是显而易见的,较重的物品(即:数字越低)下沉到底部,打火机(数字越高)越过顶部。

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

这意味着你的SQL看起来像这样:

ORDER BY 'weight', 'itemName'
希望有所帮助。

答案 3 :(得分:3)

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

我制作了这个文档,描述了我们如何实现排序顺序的存储,包括postgresql中的一个例子。请随时发表评论!

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

答案 4 :(得分:3)

以下是使用公用表表达式(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 

答案 5 :(得分:1)

(抱歉英语。我正在学习)

很简单。你需要“基数洞”

结构你需要有2列

1)pk = 32bit int

2)order = 64bit bigint(BIGINT,not DOUBLE !!!)

插入/更新

1)当您插入第一个新记录时,您必须设置order = round(max_bigint / 2)。

2)如果你在表格的开头插入,你必须设置order = round(“第一记录的顺序”/ 2)

3)如果你在表格的末尾插入,你必须设置order = round(“max_bigint - 最后记录的顺序”/ 2)

4)如果你插入中间你必须设置order = round(“之前的记录顺序 - 记录之后的顺序”/ 2)

这种方法有很大的忠诚度。如果你有约束错误,或者你认为你有小基数,你可以重建订单栏(规范化)。

在具有归一化的最大化情况下(使用此结构),您可以在32位中获得“基数洞”。

非常简单快捷!

记住没有双倍!!!只有INT - order是精度值!