假设我在购物网站的数据库中有一个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
那么聪明人会做些什么呢?
修改
我需要项目的订单栏足以进行排序而不涉及辅助密钥。单独的订单列必须为其记录指定唯一的位置。
除此之外,我想知道我是否可以实现类似链接列表的内容:“下一个”列而不是“订单”列以保留下一个项目ID。但我不知道如何编写以正确顺序检索记录的查询。如果有人也对这种方法有所了解,请分享。
答案 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是精度值!