我会简单地修改切换OrderNumber
值的脚本,以便它正确地执行它而不依赖它们没有间隙。
我不知道您的脚本接受哪些参数以及如何使用它们,但我最终想出的那个接受要移动的项目的 ID 和要移动的位置数(负值表示“朝向较低的OrderNumber
值”,正值表示相反的方向)。
思路如下:
查找指定项目的OrderNumber
.
OrderNumber
从第二个参数确定的方向开始对所有项目进行排名。指定项目因此获得 的排名1
。
选择排名从1
到第二个参数的绝对值加一的项目。(即最后一项是指定项被移动到的项。)
将结果集与自身连接,以便每一行与下一行连接,最后一行与第一行连接,从而使用一组行来更新另一行。
这是实现上述内容的查询,注释解释了一些棘手的部分:
已编辑:修复了错误重新排序的问题
/* these are the arguments of the query */
DECLARE @ID int, @JumpBy int;
SET @ID = ...
SET @JumpBy = ...
DECLARE @OrderNumber int;
/* Step #1: Get OrderNumber of the specified item */
SELECT @OrderNumber = OrderNumber FROM atable WHERE ID = @ID;
WITH ranked AS (
/* Step #2: rank rows including the specified item and those that are sorted
either before or after it (depending on the value of @JumpBy */
SELECT
*,
rnk = ROW_NUMBER() OVER (
ORDER BY OrderNumber * SIGN(@JumpBy)
/* this little "* SIGN(@JumpBy)" trick ensures that the
top-ranked item will always be the one specified by @ID:
* if we are selecting rows where OrderNumber >= @OrderNumber,
the order will be by OrderNumber and @OrderNumber will be
the smallest item (thus #1);
* if we are selecting rows where OrderNumber <= @OrderNumber,
the order becomes by -OrderNumber and @OrderNumber again
becomes the top ranked item, because its negative counterpart,
-@OrderNumber, will again be the smallest one
*/
)
FROM atable
WHERE OrderNumber >= @OrderNumber AND @JumpBy > 0
OR OrderNumber <= @OrderNumber AND @JumpBy < 0
),
affected AS (
/* Step #3: select only rows that need be affected */
SELECT *
FROM ranked
WHERE rnk BETWEEN 1 AND ABS(@JumpBy) + 1
)
/* Step #4: self-join and update */
UPDATE old
SET OrderNumber = new.OrderNumber
FROM affected old
INNER JOIN affected new ON old.rnk = new.rnk % (ABS(@JumpBy) + 1) + 1
/* if old.rnk = 1, the corresponding new.rnk is N,
because 1 = N MOD N + 1 (N is ABS(@JumpBy)+1),
for old.rnk = 2 the matching new.rnk is 1: 2 = 1 MOD N + 1,
for 3, it's 2 etc.
this condition could alternatively be written like this:
new.rnk = (old.rnk + ABS(@JumpBy) - 1) % (ABS(@JumpBy) + 1) + 1
*/
注意:这里假定 SQL Server 2005 或更高版本。
此解决方案的一个已知问题是,如果指定的 ID 不能精确地移动指定数量的位置(例如,如果您想将最上面的行向上移动任意数量的位置,或者第二行两个或多个位置等)。