2017-10-08 150 views
1

對不起,如果標題錯過了,我不能想出一個更好的與我的問題有關的問題。MySQL觸發器通過重新排序其值來更新列

我一直在試圖解決這一段時間,我找不到解決方案。

我有一個表categories

+----+--------+----------+ 
| ID | Name | Position | 
+----+--------+----------+ 
| 1 | Dogs |  4 | 
| 2 | Cats |  3 | 
| 3 | Birds |  10 | 
| 4 | Others |  2 | 
+----+--------+----------+ 

我需要保持排名列,以便在某種程度上不會錯過的價值觀一樣,所以最終的表應該是這樣的:

+----+--------+----------+ 
| ID | Name | Position | 
+----+--------+----------+ 
| 1 | Dogs |  3 | 
| 2 | Cats |  2 | 
| 3 | Birds |  4 | 
| 4 | Others |  1 | 
+----+--------+----------+ 

我試過的是在UPDATE和INSERT上創建一個觸發器來試圖防止這種情況。我創建的觸發器(與INSERT相同):

DELIMITER // 
CREATE TRIGGER sortPostions BEFORE UPDATE ON categories 
FOR EACH ROW BEGIN 
    SET @max_pos = 0; 
    SET @min_pos = 0; 
    SET @max_ID = 0; 
    SET @min_ID = 0; 
    SELECT position, id INTO @max_pos,@max_ID FROM categories WHERE position = (SELECT MAX(position) FROM categories); 
    SELECT position, id INTO @min_pos,@min_ID FROM categories WHERE position = (SELECT MIN(position) FROM categories); 

    IF NEW.position >= @max_pos AND NEW.id != @max_ID THEN 
    SET NEW.position = @max_pos + 1; 
    END IF; 

    IF NEW.position <= @min_pos AND NEW.id != @min_ID THEN 
    SET NEW.position = @min_pos - 1; 
    END IF; 

    IF NEW.position < 0 THEN 
    SET NEW.position = 0; 
    END IF; 

END// 
DELIMITER ; 

但不幸的是,它不能按預期工作。這不是修復缺失值,我認爲這不是一個完美的解決方案。

我繼續和創建的過程:

BEGIN 
    SET @n = 0; 
    UPDATE categories 
    SET position = @n:[email protected]+1 
    ORDER BY position ASC; 
END 

但我無法從觸發器調用這個過程,因爲它似乎是,MySQL不允許。我得到以下錯誤:

#1442 - Can't update table 'categories' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. 

的mysql -V輸出:

mysql Ver 14.14 Distrib 5.5.57, for debian-linux-gnu (x86_64) using readline 6.3 

什麼是解決這個問題的完美解決方案? 非常感謝!

+0

我不知道在調用存儲過程從觸發器的MySQL限制。 –

+0

@GordonLinoff我收到以下錯誤:#1442 - 無法更新存儲的函數/觸發器中的表'類別',因爲它已被調用此存儲的函數/觸發器的語句使用。 – Lambasoft

+0

@GordonLinoff我認爲這是因爲觸發器/過程進入一個無限循環,程序再次觸發觸發器。 – Lambasoft

回答

1

你不能在觸發器中做到這一點。 MySQL不允許你在產生觸發器的同一張表中對觸發器(或觸發器所調用的過程)執行INSERT/UPDATE/DELETE。

原因是它可能會導致無限循環(您的更新會產生觸發器,它會更新表,這會再次產生觸發器,從而再次更新表)。另外,如果這些請求中有多個請求同時發生,它可能會產生鎖定衝突。

如果您必須重新編號行的位置,您應該在應用程序代碼中執行此操作。在您的初始查詢完成後,使用單獨的語句執行此操作。

另一種選擇是不必擔心連續的職位。只要確保他們是在正確的順序。然後,當您查詢表格時,按需生成行號。

SELECT (@n:[email protected]+1) AS row_num, c.* 
FROM (SELECT @n:=0 AS n) AS _init 
CROSS JOIN categories AS c 
ORDER BY c.position ASC; 

+---------+----+--------+----------+ 
| row_num | id | name | position | 
+---------+----+--------+----------+ 
|  1 | 4 | Others |  2 | 
|  2 | 2 | Cats |  3 | 
|  3 | 1 | Dogs |  4 | 
|  4 | 3 | Birds |  10 | 
+---------+----+--------+----------+ 

在MySQL 8.0中,你就可以使用ROW_NUMBER()更標準的語法來做到這一點。

SELECT ROW_NUMBER() OVER w AS row_num, c.* 
FROM categories AS c 
WINDOW w AS (ORDER BY c.position ASC); 

給出與使用用戶變量的查詢相同的輸出。

+0

感謝您的回答!你認爲我可以創建一個SELECT觸發器來動態添加row_num列(您提供的查詢)?我應該將它放在SELECT查詢之後或之前?剛剛意識到你是Oracle主管,我很幸運:) – Lambasoft

+0

沒有SELECT觸發器。可能你想要的是[VIEW](https://dev.mysql.com/doc/refman/5.7/en/create-view.html)。這是一種存儲類似宏的複雜查詢類型的方法。然後你可以像查看簡單的表一樣查詢視圖。但要小心閱讀如何在視圖中使用ORDER BY。 –