2012-07-03 54 views
0

SQL Server表中的列:ID(PK,自動遞增),ORDERNUMBERCol1中col2的 ..SQL Server自定義排序記錄表,允許刪除記錄與自定義排序

默認情況下,插入觸發器將建議的here中的值從ID複製到OrderNumber。 使用一些可視化界面,用戶可以通過遞增或遞減OrderNumber值來對記錄進行排序。

然而,如何處理被記錄在此期間刪除?

示例: 假設您添加具有PK ID的記錄:1,2,3,4,5 - OrderNumber接收相同的值。然後刪除ID = 4,ID = 5的記錄。下一條記錄將具有ID = 6,並且OrderNumber將收到相同的值。 跨度爲2的OrderNumbers將強制用戶減少ID = 6的記錄,如3次,以更改其訂單(即按下3x按鈕)。

另一種方法是,可以將select count(*) from table插入到OrderNumber中,但是當刪除一些舊行時,它允許在表中有幾個相似的值。

如果不刪除記錄,但只有「取消」他們,他們仍然包含在排序順序,只爲用戶不可見的。目前,需要Java解決方案,但我認爲問題是語言無關的。

是否有這個更好的辦法?

+0

我意識到這可能聽起來毫不相關,但您現在執行的訂單改變如何?你在談論多次遞減'OrderNumber'值的必要性,而不是一次。但是,如果您只需要將其減少一次,那將如何工作?當然,你還需要在另一行增加* OrderNumber(可能是'OrderNumber = 5'的那個),對嗎?那是你怎麼做的?如果是這樣,*你在SQL中還是在應用程序中執行此操作?如果在SQL中,你能顯示腳本嗎? –

+0

@AndriyM在檢查(在java servlet中)如果該行不是第一個或最後一個,我正在運行兩個SQL UPDATE查詢來切換這2個元素的OrderNumber。所以,現在,這只是一個元素的轉變。我猜如果我要將元素移動幾個地方,我需要在起點和終點之間更新所有的OrderNumbers。 – yosh

回答

1

我想簡單地修改,以便它正確做它沒有依靠他們的存在無間隙用於切換OrderNumber值腳本。

我不知道你的腳本接受什麼樣的參數以及它如何使用他們,但我已經最終一個想出接受該項目的ID移動和位置被移動的數量(負價值意味着「朝向更低的OrderNumber值」,而正值意味着相反的方向)。

的思路如下:

  1. 查找指定項目的OrderNumber

  2. 排名均來自於由第二個參數確定的方向OrderNumber開始的項目。指定的項目因此得到1的排名。

  3. 挑選排名從1到第二個參數的絕對值加1的項目。 (即最後一個項目是指定項目被移動到的那個項目。)

  4. 將自己的結果集合加入,以便每一行都與下一行連接,最後一行與第一行連接,從而使用一組行來更新另一行。

這是實現上述查詢,用註釋解釋了一些棘手的部分:

編輯:固定不正確的重新排序

/* 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, 
      [email protected], 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無法完全移動指定的位置數量,它將不會正確「移動」行(例如,如果要將最上面一排向上移動任何數字的位置,或第二排兩個或更多個位置等)。

+0

令人印象深刻的排序解決方案,但插入和刪除呢?當我從幾個小時前刪除一些記錄。如果存在差距,我將什麼OrderNumber分配給新記錄?不能真的是ID,不能算(*)我猜。 – yosh

+0

插入行時,使用它們的ID來初始化它們的OrderNumbers,就像我現在明白的那樣。如果行被刪除,則對OrderNumbers執行任何操作。即使有差距,這個解決方案也應該像OrderNumbers一樣處理它們。即如果OrderNumber的值爲10,而緊接着的OrderNumber的值爲7,並且您需要兩者交換位置,則此腳本將正確執行此操作。您只需指定兩個項目之一的ID和要移動的位置的數量(在此情況下爲1或-1,具體取決於您指定的項目)。 –

+0

對,它完全忽略了差距。與我正在考慮的方法有點不同。我會盡快檢查,謝謝。 – yosh

0

好了 - 如果我沒有記錯的話,要進行碎片整理您的訂單號碼。 如果您爲此使用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) 
+0

我應該在每次刪除之後運行它來「修補」缺失的記錄。但它並沒有真正解決很多..我如何獲得下一個OrderNumber輸入下一個插入? ID可能比當前的OrderNumber更高,並且具有多個相似值的count(*)問題並沒有真正解決。 – yosh

+0

也許這些行應該按照'OrderNumber'而不是'ID'來排列,否則順序將隨着每個INSERT被重置(或者您打算如何運行它,當然不是在DELETE上,通過使用來判斷插入「)。 –

+0

也許這將是一個解決方案..每次刪除(更可能最多5個記錄每週刪除)再次分配OrderNumber到每個元素。但是,像@AndriyM注意到的那樣,行應該按照舊的OrderNumber列排序。雖然,我不確定是否每次刪除更新數千行都不會有點過分。 – yosh

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條記錄的順序。


這是我的問題一個有效的解決方案,但是this one也是很不錯的一個,也許對別人更有用。