2017-08-02 28 views
1

我有一個MariaDB(想想MySQL)數據庫,其中行有position字段。此位置可改變,但必須是連續的,並且在開始1更有效的維護數據庫中的行順序字段的方法?

簡表模式:

CREATE TABLE `ordered_data` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `owner` int(11) NOT NULL, 
    `data` varchar(300) COLLATE utf8_unicode_ci NOT NULL, 
    `position` int(11) NOT NULL 
    PRIMARY KEY (`id`) 
) 

因此,例如,用戶可能想用ID10的行移動到位置#1 。這當然需要對所有後續項目重新排序,否則會在position字段中重複。我不能找出一個方法來做到這一點在少於4個查詢。

我目前的解決方案在下面,它非常簡單,但我不禁感到有更優雅的方式來執行重新排序。在這個例子中,我運動行與ID32,由用戶20,定位3

  1. SELECT position FROM ordered_data WHERE id = 32獲取currentPosition
  2. 填補我們即將與UPDATE ordered_data SET position = position - 1 WHERE position > currentPosition AND owner = 20之間的差距。
  3. UPDATE ordered_data SET position = position + 1 WHERE position >= 3 AND owner = 20爲該行的新位置(3)騰出空間。
  4. 更新該行的位置以適應差距UPDATE ordered_data SET position = 3 WHERE id = 32

所有建議非常歡迎。將前兩個語句與子查詢結合在一起使MySQL抱怨使用同一個表進行更新和選擇。

每個擁有者不可能有超過10行。

+1

每組10行,我不關心性能,並使用最簡單(最易讀)的解決方案。你已經有了。我只是將順序改爲1,3,4,2 - 這樣它也可以在'(owner,position)'上使用唯一鍵。 –

+0

@PaulSpiegel在重新訂購工作中有一些麻煩(直接交換會留下空白,並且讓我的頭部繞過倉位很棘手),但好主意在於:在數據庫級別執行它。 – JakeSteam

回答

1

改變步驟的順序並不像我想的那麼簡單。但是再增加一步,您可以使用(owner, position)上的唯一鍵對算法進行操作。爲避免步驟2中出現重複輸入錯誤,您可以暫時將possition = 0指定給您想要移動的項目。完整的算法是這樣的:

set @owner = 20; 
set @id = 32; 
set @new_pos = 3; 

-- 1. get current position 
set @old_pos = (select position from ordered_data where id = @id); 

-- 1.1 "remove" the item from its old position 
update ordered_data set position = 0 where id = @id; 

-- 2. close the gap at the old position 
update ordered_data 
set position = position - 1 
where position owner = @owner 
    and position > @old_pos 
order by position asc -- important for unique key 

-- 3. make space at the new position 
update ordered_data 
set position = position + 1 
where position owner = @owner 
    and position >= @new_pos 
order by position desc -- important for unique key 

-- 4. set new position 
update ordered_data set position = @new_pos where id = @id; 

這種方法是(恕我直言)的罰款爲小團體。對於更大的數據集,步驟3和步驟4可以進行優化並一步完成。看看下面的例子:你有一個100萬個物品的組,並且你想把一個物品從位置7移動到位置3.在從物品位置「移除」物品之後,我們將位置8更新爲1000000,並將它們遞減1縮小差距。然後我們更新位置3至999999並增加它們以騰出空間。這幾乎更新了整個集團的兩倍,而我們需要的是3位增加至6爲了實現這一目標,步驟2和3可以

if (@new_pos < @old_pos) 
    update ordered_data 
    set position = position + 1 
    where position owner = @owner 
     and position between @new_pos and @old_pos 
    order by position desc 
else if (@new_pos > @old_pos) 
    update ordered_data 
    set position = position - 1 
    where position owner = @owner 
     and position between @old_pos and @new_pos 
    order by position asc 
else 
    -- do nothing 

注意更換:這是僞代碼。您需要在應用程序站點選擇正確的查詢。

,你甚至可以將它們合併到一個單一的查詢:

update ordered_data 
    set position = position + sign(@new_pos - @old_pos) 
    where position owner = @owner 
     and position between @new_pos and @old_pos 
    order by position * sign(@new_pos - @old_pos) desc 

但是,在這種情況下,發動機將可能無法使用索引用於GROUP BY子句。

+0

嘿,這是通過PDO(PHP MySQL)完成的,這意味着更復雜的查詢並不容易實現(雖然當然可以通過存儲過程等)。第一種方法(暫時將ID設置爲0)可行,但如果發生多次重新排序,我會有點擔心獨特衝突的(非常小的)機會。儘管如此,Tyvm將繼續保持這種情況,以防數據增長超出預期。 V有幫助! – JakeSteam

相關問題