値を切り替えるスクリプトを変更するだけで、ギャップがないことに依存せずOrderNumber
に正しく動作するようになります。
あなたのスクリプトがどの引数を受け入れ、どのように使用するかはわかりませんが、最終的に思いついたものは、移動するアイテムの ID と移動する位置の数を受け入れます (負の値は "より低い値に向かってOrderNumber
」、正の値は反対方向を意味します)。
考え方は次のとおりです。
指定されたアイテムの を検索しますOrderNumber
。
OrderNumber
2 番目の引数によって決定される方向から始まるすべてのアイテムをランク付けします。したがって、指定された項目は のランキングを受け取ります1
。
1
第 2 引数の絶対値に 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 以降のバージョンを想定しています。
このソリューションの既知の問題の 1 つは、指定された ID を指定された位置数だけ正確に移動できない場合 (たとえば、一番上の行を任意の数だけ上に移動したい場合、または2 つ以上の位置で 2 列目など)。