2012-08-02 82 views
0

我有一個計數表,我希望能夠在重命名其中一個複合鍵字段時計算總計。這是很難解釋,所以讓我告訴你一些SQL:當在MySQL中執行UPDATE時,重複鍵上的`counts`列

示例表:

CREATE TABLE `test` (
    `id` int(11) NOT NULL, 
    `type` int(11) NOT NULL, 
    `count` int(11) NOT NULL, 
    PRIMARY KEY (`id`,`type`) 
); 

插入一些數據:

INSERT INTO test VALUES(1, 10, 1); 
INSERT INTO test VALUES(2, 20, 3); 
INSERT INTO test VALUES(2, 10, 3); 
INSERT INTO test VALUES(2, 30, 3); 

查詢數據:

mysql> SELECT SUM(count) as count FROM test WHERE id = 2; 
+-------+ 
| count | 
+-------+ 
|  9 | 
+-------+ 

mysql> SELECT SUM(count) as count FROM test WHERE type = 10; 
+-------+ 
| count | 
+-------+ 
|  4 | 
+-------+ 

工作得很好,快速靈活。現在

,我想重映射型10鍵入20

UPDATE test SET type = 20 WHERE type = 10; 
Duplicate entry '2-20' for key 'PRIMARY' 

使用ON DUPLICATE KEY UPDATE這裏是無效的。 我認爲它應該有可能與創意插入,但我不知道。任何人都可以想到一種方法嗎?

回答

1

一種方法是「鬆開」PRIMARY KEY,即將其從主鍵(唯一)改爲非唯一鍵。這將允許重複的值,並允許您的UPDATE語句成功,這樣您將有兩個(或更多)具有匹配(id,type)的行。 (請注意,這使得更新單行問題,所以你可能會想添加一個新列,它是唯一的。AUTO_INCREMENT列會很好地工作了點。)


如果你不想那麼另一種方法是將類型10和類型20行的計數(對於每個id)「合併」爲類型20行,將類型10行的計數設置爲零,並且(可選地)在單獨的語句中刪除冗餘類型10行。

下面的語句通過在第一次選擇中將10值「映射」到20來「結合」類型10和類型20計數。


-- combine count for types 10 and 20 as new count for type 20 
-- and set count to zero for type 10 

INSERT INTO test (id, `type`, `count`) 
SELECT t.id 
     , IF(t.`type`=10,20,t.`type`) AS new_type 
     , SUM(t.`count`) AS `count` 
    FROM test t 
    WHERE t.`type` IN (10,20) 
    GROUP BY id, new_type 
    UNION ALL 
SELECT u.id 
     , u.`type` 
     , 0 AS `count` 
    FROM test u 
    WHERE u.`type` = 10 
    ON DUPLICATE KEY UPDATE `count` = VALUES(`count`); 

-- remove unnecessary type 10 rows. 
DELETE FROM test WHERE `type` = 10 AND `count` = 0; 

注:在首先選擇IF()表達式等價於:

CASE WHEN t.type = 10 THEN 20 ELSE t.type END 

第二個選擇是讓我們所有類型的10行,將需要被更新。我們使用UNION ALL運算符連接這兩個結果集。

然後,我們將組合(連接)的結果集,並將它們運行到插入。在任何我們點擊「重複」鍵的地方,我們通過ON DUPLICATE KEY子句執行更新操作。

在執行DELETE之前,您可能希望在事務中執行此操作(如果您使用的是InnoDB)並驗證第一條語句是否成功完成。如果第一條語句拋出一個異常(也許有一個不可思議的觸發器),那麼你會想ROLLBACK該事務。如果DELETE由於某種原因失敗,也可能需要ROLLBACK(可能存在違反外鍵約束)。

或者,您不一定需要執行DELETE,您可以將類型計數爲零的10行。


重要:

沒有實現這兩種解決方案!其中只有一個。

第一種方法是模式更改,它不需要任何數據更改。該更改將允許您的更新成功。

第二種方法要求模式保持不變,並取決於(id,type)上UNIQUE KEY的存在(和強制執行)。

+0

這正是我正在尋找的東西,我有一種感覺,它可以用純SQL。不幸的是,後來我意識到整個方法都行不通,因爲我們將'未映射'的計數存儲爲'0'類型,並且無法知道它們屬於哪個'id'。儘管如此,這個答案完全正確,+1 – 2012-08-10 13:39:33

1

當您更新您的查詢時,您創建了id = 2type = 20的多行,由於您的primary key約束條件,這是不允許的。

您應該改爲使用單個列作爲主鍵,並在插入新行時使其自動遞增。這樣保證了它的獨特性。

+0

請問爲什麼這是低票一次? – Matt 2012-08-03 12:37:45

0

你已經在兩列上設置了主鍵,更新後第三行的主鍵將與第二行相同,所以這是不允許的。

最後你會有1..20; 2..20,2..20等