2011-05-03 50 views
3

的重排序我有一個表的複合主鍵:就地一個主鍵列

table MyTable 
(
    some_id smallint not null, 
    order_seq smallint not null, 
    --other columns 
) 

...其中some_idorder_seq組成的複合主鍵。 order_seq列用於確定我的C#3.5/ASP.NET應用程序的其他部分中的顯示順序。

我設置了一個管理頁面,在這裏用戶可以洗牌周圍這些行,一次改變多行order_seq值:

screenshot

屏幕上的所有項目具有相同的some_id當用戶單擊提交時,新的order_seq值應該被保存到數據庫。

我的問題是order_seq是PK的一部分,所以將這些東西混洗的任務變得複雜。由於some_id是相同的,我無法識別它們,除了order_seq本身,它隨着我的變化而變化。此外,我必須確保order_seq值保持唯一(推測具有某種臨時值)。

我最好的想法是使用某種內存集合來跟蹤我迄今爲止所做的更改。儘管如此,我卻遇到了麻煩。我怎樣才能一次執行多行的原地重新排序?無論是基於C#還是基於SQL的解決方案,我都可以。

編輯:不幸的是,我對錶格設計,和對數據庫一般控制有限的零控制。我將無法更改PK方案或引入任何新欄目。

+0

它是否是一個複合PK? – Oded 2011-05-03 20:36:13

+0

必須把它扔在那裏,那麼完全砍掉組合鍵,然後放下一個guid列,然後將它們用作查找而不是PK呢? – 2011-05-03 20:36:48

+1

即使是身份專欄,也會讓你的生活更輕鬆。 – 2011-05-03 20:38:45

回答

2

如果可能的話,如果您引入代理主鍵,您將爲自己節省很多壓力。由於您可以在不更改任何其他字段的情況下更改order_seq,因此它不適合作爲主鍵的一部分(記錄中的所有其他字段應直接依賴)。但即使超出了理論的範圍,你已經發現了這種複合鍵的技術問題。

如果您確實需要保持結構相同,那麼更改關鍵字段將會非常乏味,並且您已經發現需要一些臨時值。關閉我的頭頂,如果我有做到這一點我做的兩個步驟:

UPDATE Table SET order_seq = @newval + BIG_OFFSET WHERE order_seq = @oldval 

UPDATE Table SET order_seq = order_seq - BIG_OFFSET WHERE order_seq > BIG_OFFSET 

顯然,運行的每個不同行的情況正在發生變化的第一條語句,然後在很運行第二個聲明結束重置它們的值。這應該可以防止您在進程中的任何時候都有重複的主鍵。

+0

我試過類似的設置'order_seq = @newval * -1',但我得到了一個PK違規錯誤。一次更改多個'order_seq'列似乎會導致問題。 – 2011-05-03 20:53:23

+0

當我發佈這個問題時,我認爲*我已經嘗試過這種方法並失敗了,但肯定還有別的事情發生。我最終設置了'order_seq = order_seq * -1 where some_id = @ some_id',然後根據這個設置將所有內容設置爲新值。它基本上與此相同,但它避免了溢出的可能性(潛在的問題,取決於偏移量)。這是最接近我使用的,所以我接受(並upvoting)它。謝謝你的幫助。 – 2011-05-03 22:02:50

1

您需要存儲原始複合PK(隱藏列)。在屏幕上,如果你正確地管理它,兩個項目不應該有相同的順序。然後,您可以找到具有原始複合PK的項目,並使用新訂單值更新它們。

編輯:好的,你可以在你的代碼中做類似的事情,即跟蹤原始PK和當前PK的對象列表,任何時候用戶試圖改變順序,做一個驗證,如果它是允許的,然後允許或阻止它。你需要找到一個更新了id和orderId的對象,如果你找到一個,那麼這是不允許的。 對不起,如果我沒有理解這個問題。試圖幫助。

+0

我已經這樣做了,只是我需要一次更改多行。當我改變它的時候,我需要一些方法來記錄它以後的情況,如果這是有道理的。 – 2011-05-03 20:54:55

+0

Re:你的編輯 - 我想這就是如果我沒有找到更簡單的解決方案,我將不得不做的。這意味着在數據庫中留下垃圾臨時數據的風險較小,但我在編碼時採取了一些措施,看起來相當複雜。 +1,但這是一個很好的建議。 – 2011-05-03 22:08:41

1

你可以用一個帶有一對舊order_seq和新order_seq的XML參數的SP來完成。

一些測試數據

create table MyTable 
(
    some_id int, 
    order_seq int, 
    name varchar(10) 
    primary key(some_id, order_seq) 
) 

insert into MyTable values 
(1, 1, '1_1'), 
(1, 2, '1_2'), 
(1, 3, '1_3'), 
(1, 4, '1_4'), 
(2, 1, '2_1'), 
(2, 2, '2_2') 

存儲過程

create procedure SetOrder 
    @some_id int, 
    @new_order xml 
as 

;with cte as 
(
    select 
    X.N.value('@OldSeq', 'int') as OldSeq, 
    X.N.value('@NewSeq', 'int') as NewSeq 
    from @new_order.nodes('/i') as X(N) 
) 
update T 
    set order_seq = C.NewSeq 
from MyTable as T 
    inner join cte as C 
    on T.order_seq = C.OldSeq 
where T.some_id = @some_id 

要顛倒順序some_id = 1呼叫SP這樣的:

exec SetOrder 1, 
    '<i OldSeq="1" NewSeq="4"/> 
    <i OldSeq="2" NewSeq="3"/> 
    <i OldSeq="3" NewSeq="2"/> 
    <i OldSeq="4" NewSeq="1"/>' 

你需要跟蹤老的order_seq並在用戶選擇提交更改時構建xml。

+0

我喜歡這種方法。我設法以一種不同的方式解決了這個特殊的問題,但是這種感覺比我不得不使用的更好,也更少,所以我會記住它。 +1和謝謝。 – 2011-05-03 22:04:48