2012-05-31 18 views
6

假設我有以下結果從一個MySQL數據庫表中設置:移位值向下一個表中的鏈

+----+------+-------+ 
| ID | type | value | 
+----+------+-------+ 
| 8 | A | 1435 | 
| 9 | B | 7348 | 
| 10 | A | 1347 | 
| 11 | A | 3478 | 
| 12 | A | 4589 | 
| 13 | B | 6789 | 
+----+------+-------+ 

我想刪除行ID 8和外地「值」推值按照這種方式,每一行都具有先前條目的值,但隻影響字段'type'與被刪除的行相同的那一行(在這種情況下爲'A')。

也就是說,刪除行ID 8應最終產生以下:

+----+------+-------+ 
| ID | type | value | 
+----+------+-------+ 
| - | - | - | * 
| 9 | B | 7348 | | 
| 10 | A | 1435 | * | 
| 11 | A | 1347 | * | 
| 12 | A | 3478 | * | 
| 13 | B | 6789 | V 
+----+------+-------+ 

ID 10已從ID 8繼承的值,然後從ID 10 ID,依此類推11所繼承。不過請注意,類型'B'的行不受影響。

所以問題:有沒有辦法執行這個值的「轉移」,而不必逐一查詢和更新每一行?在一個理想的世界中,我會做一個查詢來完成移位,然後再執行另一個查詢來刪除行,但我不確定這是否可行。

(此外,我寧可不使用觸發器,因爲我打算在應用程序中封裝了所有的應用程序邏輯)

+0

在查詢?我對此表示懷疑。每個更新都是一個新的查詢。我會推薦一個SQL過程,但如果你不想要那些,我想你堅持循環構造。爲了加快速度,請使用交易。 – clentfort

+0

我認爲可以收集所有需要更新的行(temptemtable)(#temptable)。從這裏,你可以計算新的值,並使用一個更新語句來更新你的主表。那麼你不需要一個循環contstruction。 –

+0

編輯:你可以使用ROW_NUMBER()來移動行。由於這在mysql中不可用,因此請在此問題上檢查OMGPonies的答案:http://stackoverflow.com/questions/1895110/row-number-in-mysql –

回答

2
SET @remove_id = 8; 

SELECT ID, type, value FROM (
    SELECT ID, 
      type, 
      CAST(IF(type <> @type OR ISNULL(@val), value, @val) AS UNSIGNED) 
      AS value, 
      @type := IF(ID = @remove_id, type, @type), 
      @val := IF(type = @type, value, @val) 
    FROM  my_table JOIN (SELECT @type := NULL, @val := NULL) AS z 
    ORDER BY ID ASC 
) AS t 
WHERE ID <> @remove_id 

看到它的sqlfiddle


UPDATE

我沒有意識到你其實是想更新基礎表。爲此,您可以使用一些輕微的hackery在UPDATE語句中有效地執行相同的操作(不能直接分配給用戶變量,因此請將分配給它的新值的串聯和從獲取新分配的用戶變量的前0個字符):

SET @remove_id = 8, @type = NULL, @val = NULL; 

UPDATE my_table SET 
    value = IF(
    type <> @type OR ISNULL(@val), 
    value, 
    CONCAT(@val, LEFT(@val := value, 0)) 
), 
    type = CONCAT(type, LEFT(
    @type := IF(
     ID <> @remove_id, 
     @type, 
     CONCAT(type, LEFT(@val := value, 0)) 
    ) 
    , 0)) 
ORDER BY ID ASC; 

DELETE FROM my_table WHERE ID = @remove_id; 

請參閱sqlfiddle

+0

Upvoted,因爲這對於單個查詢來說非常酷,但我實際上是在尋找一些不僅可以選擇而且將結果記錄回數據庫的東西。 – Mahn

+0

@Mahn:我沒有意識到這一點。看到我的更新答案有點黑客,但希望有一個簡單的解決方案比你接受的答案。 :) – eggyal

+0

美麗,謝謝。看起來在查詢本身中有很多條件表達式可以處理,我應該多看看它們。 – Mahn

0

我會建議你使用InnoDB表,所以你可以在一個事務中運行2個查詢。 我這樣做:

Step 1: Start a transaction on the table 
Step 2: Get the record that is to be deleted (e.g. ID #8) 
Step 3: Issue a query DELETE FROM tablename WHERE `ID`=$record_id 
Step 4: Issue a query UPDATE tablename SET `value`='former_value' WHERE `type`='former_type' LIMIT 1 
Step 5: if all operations were successful, commit the transaction else rollback 
Step 6: End the transaction 

希望這有助於

+0

即使使用事務,幾個查詢仍然意味着多次訪問到我想要避免的DB。感謝您的回覆。 – Mahn

2

使用窗口/分析功能很容易完成此任務。由於MySQL沒有這些,所以可以用下面的限制來模擬它們:

  • 你應該有一個唯一的字段進行排序。

我爲此使用了id

下面的查詢將enumarte每一行的表,使用type作爲分區指標:

SELECT t.id, t.type, t.value, 
    (SELECT count(*) FROM testbed WHERE type = t.type AND id <= t.id) AS rownum 
    FROM testbed t 
ORDER BY t.type, t.id; 

我只對公開程度增加ORDER BY,在最終的查詢不是必需的。

下一頁查詢允許你加入2周的結果,並有辦法「移值」所需要的方式:

SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value, 
     p.id AS p_id, p.type AS p_type, p.value AS p_value 
    FROM (SELECT t.id, t.type, t.value, 
       (SELECT count(*) FROM testbed 
       WHERE type = t.type AND id <= t.id) AS rownum 
      FROM testbed t) AS c 
    LEFT JOIN (SELECT t.id, t.type, t.value, 
        (SELECT count(*) FROM testbed 
         WHERE type = t.type AND id <= t.id) AS rownum 
       FROM testbed t) AS p 
     ON c.type = p.type AND c.rownum = p.rownum + 1 
ORDER BY c.type, c.id; 

最後,你的任務是用以下2個查詢來完成,UPDATEDELETE

UPDATE testbed AS t 
JOIN (
    SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value, 
     p.id AS p_id, p.type AS p_type, p.value AS p_value 
    FROM (SELECT t.id, t.type, t.value, 
       (SELECT count(*) FROM testbed 
        WHERE type = t.type AND id <= t.id) AS rownum 
      FROM testbed t) AS c 
    LEFT JOIN (SELECT t.id, t.type, t.value, 
         (SELECT count(*) FROM testbed 
         WHERE type = t.type AND id <= t.id) AS rownum 
       FROM testbed t) AS p 
      ON c.type = p.type AND c.rownum = p.rownum + 1 
) AS s 
    ON t.id = s.c_id 
    SET t.value = s.p_value 
WHERE t.value = 'A'; -- you can use more complex predicate(s) here 

DELETE FROM testbed WHERE id = 8; -- make sure both predicate(s) match 

您可以在SQL Fiddle(不是更新)上檢查此查詢。

+0

這是訣竅,謝謝!雖然我擔心它可能會成爲一種痛苦,但它可能會達到它所能達到的效果。 – Mahn

+0

我接受了以前的答案,因爲我重視簡潔(更容易維護),但你仍然有我的讚賞。 – Mahn

相關問題