0

カスタム ソートを使用する SQL Server テーブルには、列ID (PK、自動インクリメント)、OrderNumberCol1Col2 ..

デフォルトでは、挿入トリガーは、ここで提案されているように ID から OrderNumber に値をコピーします。ユーザーは、視覚的なインターフェイスを使用して、OrderNumber 値をインクリメントまたはデクリメントすることでレコードを並べ替えることができます。

しかし、その間に削除されたレコードをどのように処理するのでしょうか?

例: PK ID: 1,2,3,4,5 のレコードを追加するとします。OrderNumber は同じ値を受け取ります。次に、ID=4、ID=5 のレコードを削除します。次のレコードは ID=6 になり、OrderNumber は同じ値を受け取ります。欠落している 2 つの OrderNumbers のスパンがあると、ユーザーは ID=6 のレコードを 3 回デクリメントして順序を変更する必要があります (つまり、ボタンを 3 回押す)。

または、OrderNumber に挿入することもできselect count(*) from tableますが、いくつかの古い行が削除されたときに、テーブルにいくつかの同様の値を持つことができます。

レコードを削除せずに「非アクティブ化」した場合でも、レコードはソート順に含まれ、ユーザーには表示されません。現時点では Java での解決が必要ですが、問題は言語に依存しないと思います。

これでより良いアプローチはありますか?

4

3 に答える 3

1

値を切り替えるスクリプトを変更するだけで、ギャップがないことに依存せずOrderNumberに正しく動作するようになります。

あなたのスクリプトがどの引数を受け入れ、どのように使用するかはわかりませんが、最終的に思いついたものは、移動するアイテムの ID と移動する位置の数を受け入れます (負の値は "より低い値に向かってOrderNumber」、正の値は反対方向を意味します)。

考え方は次のとおりです。

  1. 指定されたアイテムの を検索しますOrderNumber

  2. OrderNumber2 番目の引数によって決定される方向から始まるすべてのアイテムをランク付けします。したがって、指定された項目は のランキングを受け取ります1

  3. 1第 2 引数の絶対値に 1 を加えた値までの順位のアイテムを選択します。(つまり、最後のアイテムは、指定されたアイテムの移動先です。)

  4. すべての行が次の行と結合され、最後の行が最初の行と結合されるように、結果のセットをそれ自体と結合し、行の 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 列目など)。

于 2012-07-04T13:46:37.770 に答える
0

わかりました-私が間違っていなければ、OrderNumberを最適化する必要があります。これに使用するとどうなりますROW_NUMBER()か?

例:

;WITH calc_cte AS (
  SELECT
    ID
    , OrderNumber
    , RowNo = ROW_NUMBER() OVER (ORDER BY ID)
  FROM
    dbo.Order    
)
UPDATE
  c
SET
  OrderNumber = c.RowNo
FROM
  calc_cte c
WHERE EXISTS (SELECT * FROM inserted i WHERE c.ID = i.ID)
于 2012-07-03T09:35:05.233 に答える
0

自分の質問に答えたくありませんでしたが、解決策を見つけたと思います。

クエリを挿入:

INSERT INTO table (OrderNumber, col1, col2) 
VALUES ((select count(*)+1 from table),val1,val2)

トリガーを削除:

CREATE TRIGGER Cleanup_After_Delete ON table
AFTER DELETE AS
BEGIN
  WITH rowtable AS (SELECT [ID], OrderNumber, rownum = ROW_NUMBER() 
                    OVER (ORDER BY OrderNumber ASC) FROM table)
  UPDATE rt SET OrderNumber = rt.rownum FROM rowtable rt 
  WHERE OrderNumber >= (SELECT OrderNumber FROM deleted)
END

トリガーは削除のたびに起動し、削除されたものより上のすべての OrderNumbers を修正します (ギャップなし)。これは、OrderNumbers を切り替えることで、2 つのレコードの順序を簡単に変更できることを意味します。


これは私の問題の有効な解決策ですが、これも非常に優れた解決策であり、おそらく他の人にとってはより便利です。

于 2012-07-09T14:20:13.377 に答える