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

Şaf*_*Gür 31 database sql-server sorting

假设我Product在购物网站的数据库中有一张桌子,用于保存商店产品的描述,价格等.使我的客户能够重新订购这些产品的最有效方法是什么?

我创建了一个Order用于排序记录的列(整数),但由于我在实际需要更改之后用于更改每个记录顺序的原始方法,这让我对性能产生了一些麻烦.一个例子:

Id    Order
5     3
8     1
26    2
32    5
120   4
Run Code Online (Sandbox Code Playgroud)

现在我该怎么做才能将记录的顺序更改ID=26为3?

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

这导致在我想要改变以便腾出空间之后更新每一条记录:

Id    Order
5     4
8     1
26    3
32    6
120   5
Run Code Online (Sandbox Code Playgroud)

那么聪明人会做些什么呢?

  • 我使用SQL Server 2008 R2.

编辑:

我需要一个项目的订单列足以进行排序,而不涉及二级密钥.单独的订单列必须为其记录指定唯一的位置.

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

xQb*_*ert 28

Update product set order = order+1 where order >= @value changed
Run Code Online (Sandbox Code Playgroud)

虽然随着时间的推移,你会在订单中获得越来越大的"空间",但它仍然会"排序"

这将在一个语句中将要更改的值和其后的每个值加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 ID=@IDToChange;
Run Code Online (Sandbox Code Playgroud)

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

  • 不知道唯一约束;可以在执行此操作时暂时将其关闭:或者通过设置“更新产品集顺序 = -1 其中 ID = @IDtoChange;”来启动该过程,然后我们做的最后一件事是将 -1 设置为我们关心的价值。通过上面的更新。遗憾的是,这假设 -1 是允许的值,并且现在在这种情况下具有“特殊含义”。(但仅在该过程(应该在事务逻辑中)运行的一小部分时间内),但由于我们的更新是基于 ID 的,并且在事务中,-1 不应导致违反约束。 (2认同)
  • 默认情况下,“BETWEEN”是不对称的,因此 BETWEEN 3 AND 2 返回空结果。我编辑了答案以反映这一点。 (2认同)

Jon*_*ler 6

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


小智 6

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

结构:您需要有 2 列:

  1. pk = 32 位整数

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

插入/更新L

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

  2. 如果在表的开头插入,则必须设置order = round("order of first record" / 2)

  3. 如果在表尾插入,则必须设置order = round("max_bigint - order of last record" / 2)

  4. 如果在中间插入,则必须设置order = round("order of record before - order of record after" / 2)

这种方法有很大的基数。如果您有约束错误或者您认为基数较小,您可以重建订单列(标准化)。

在标准化的最大值情况下(使用此结构),您可以在 32 位中出现“基数空洞”。

非常简单快捷!

记住不要双重!只有 INT - 顺序才是精度值!

  • 但在插入 64 行后,您将用完“order”的值(order 值将小于 1) (6认同)

Pse*_*nja 5

我过去使用的一种解决方案(取得了一些成功)是使用“重量”而不是“顺序”。重量很明显,较重的物品(即:数字越小)沉入底部,越轻(数字越大)则升至顶部。

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

这意味着您的SQL将如下所示:

ORDER BY 'weight', 'itemName'
Run Code Online (Sandbox Code Playgroud)

希望能有所帮助。


cwi*_*lls 5

这是使用公用表表达式 (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 
Run Code Online (Sandbox Code Playgroud)