2015-11-03 175 views
5

我對重複更新查詢插入有點困惑。 我有MySQL的表結構是這樣的:針對非主鍵密鑰的重複更新MySQL插入

  • RECORD_ID(PRIMARY,UNIQUE)
  • 爲person_id(唯一的)
  • SOME_TEXT
  • some_other_text

我想更新SOME_TEXT和some_other_text如果它的id存在於我的table.person中,或者在此表中插入新記錄,則返回值。如果person_id不是主要的,它如何完成?

回答

5

您需要一個查詢來檢查record_id(或person_id)是否存在任何行。如果存在更新,否則插入新行

IF EXISTS (SELECT * FROM table.person WHERE record_id='SomeValue') 
    UPDATE table.person 
    SET some_text='new_some_text', some_other_text='some_other_text' 
    WHERE record_id='old_record_id' 
ELSE 
    INSERT INTO table.person (record_id, person_id, some_text, some_other_text) 
    VALUES ('new_record_id', 'new_person_id', 'new_some_text', 'new_some_other_text') 

另一種更好的方法是

UPDATE table.person SET (...) WHERE person_id='SomeValue' 
IF ROW_COUNT()=0 
    INSERT INTO table.person (...) VALUES (...) 
+0

[@@ ROWCOUNT](https://msdn.microsoft.com/en-us/library/ms187316.aspx)或[ROW_COUNT()](https://dev.mysql.com/doc/refman/) 5.7/EN /信息functions.html#function_row數)? – wchiquito

+0

@@ ROW_COUNT()抱歉。 @@ ROWCOUNT for SQL Server @wchiquito – fattidare

+0

@fattidare檢查是否存在另一個工作解決方案 - 沒關係,謝謝 – moonvader

3

13.2.5.3 INSERT ... ON DUPLICATE KEY UPDATE Syntax

如果指定了對重複密鑰更新,和行插入該 會在UNIQUE索引或PRIMARY KEY中導致重複值,MySQL 執行舊行的UPDATE。

例子:

DELIMITER // 

DROP PROCEDURE IF EXISTS `sp_upsert`// 
DROP TABLE IF EXISTS `table_test`// 

CREATE TABLE `table_test` (
    `record_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    `person_id` INT UNSIGNED NOT NULL, 
    `some_text` VARCHAR(50), 
    `some_other_text` VARCHAR(50), 
    UNIQUE KEY `record_id_index` (`record_id`), 
    UNIQUE KEY `person_id_index` (`person_id`) 
)// 

INSERT INTO `table_test` 
    (`person_id`, `some_text`, `some_other_text`) 
VALUES 
    (1, 'AAA', 'XXX'), 
    (2, 'BBB', 'YYY'), 
    (3, 'CCC', 'ZZZ')// 

CREATE PROCEDURE `sp_upsert`(
    `p_person_id` INT UNSIGNED, 
    `p_some_text` VARCHAR(50), 
    `p_some_other_text` VARCHAR(50) 
) 
BEGIN 
    INSERT INTO `table_test` 
    (`person_id`, `some_text`, `some_other_text`) 
    VALUES 
    (`p_person_id`, `p_some_text`, `p_some_other_text`) 
    ON DUPLICATE KEY UPDATE `some_text` = `p_some_text`, 
          `some_other_text` = `p_some_other_text`; 
END// 

DELIMITER ; 

mysql> CALL `sp_upsert`(1, 'update_text_0', 'update_text_1'); 
Query OK, 2 rows affected (0.00 sec) 

mysql> SELECT 
    -> `record_id`, 
    -> `person_id`, 
    -> `some_text`, 
    -> `some_other_text` 
    -> FROM 
    -> `table_test`; 
+-----------+-----------+---------------+-----------------+ 
| record_id | person_id | some_text  | some_other_text | 
+-----------+-----------+---------------+-----------------+ 
|   1 |   1 | update_text_0 | update_text_1 | 
|   2 |   2 | BBB   | YYY    | 
|   3 |   3 | CCC   | ZZZ    | 
+-----------+-----------+---------------+-----------------+ 
3 rows in set (0.00 sec) 

mysql> CALL `sp_upsert`(4, 'new_text_0', 'new_text_1'); 
Query OK, 1 row affected (0.00 sec) 

mysql> SELECT 
    -> `record_id`, 
    -> `person_id`, 
    -> `some_text`, 
    -> `some_other_text` 
    -> FROM 
    -> `table_test`; 
+-----------+-----------+---------------+-----------------+ 
| record_id | person_id | some_text  | some_other_text | 
+-----------+-----------+---------------+-----------------+ 
|   1 |   1 | update_text_0 | update_text_1 | 
|   2 |   2 | BBB   | YYY    | 
|   3 |   3 | CCC   | ZZZ    | 
|   5 |   4 | new_text_0 | new_text_1  | 
+-----------+-----------+---------------+-----------------+ 
4 rows in set (0.00 sec) 

SQL Fiddle demo

+0

我認爲這應該是正確的答案。它效率更高,並且符合MySQL批准的方式。 – reydelleon

4

你提的問題是非常有效的。這是一個非常普遍的要求。由於MySQL提供的內容,大多數人都錯了。

  • 要求:插入,除非PRIMARY鍵存在,否則更新
  • 常見的方法:ON DUPLICATE KEY UPDATE
  • 這種做法的結果,令人不安:插入,除非PRIMARY或任何UNIQUE存在,否則更新

ON DUPLICATE KEY UPDATE有什麼可怕的錯?你插入一個新的記錄,並帶有一個新的PRIMARY鍵值(比如一個UUID),但是你碰巧有一個UNIQUE鍵的重複值。

你想要的是一個適當的例外,表明你試圖插入一個副本到UNIQUE列。

但是你得到的是一個不需要的UPDATE! MySQL將採取相沖突的記錄並開始覆蓋其值。如果意外發生這種情況,則會損壞舊記錄,並且對舊記錄的任何傳入引用現在都會引用新記錄。由於您可能不會通知查詢來更新PRIMARY列,因此無法找到您的新UUID。如果你遇到過這些數據,它可能沒有意義,你也不知道它來自哪裏。

我們需要一個解決方案,以實際上插入除非PRIMARY鍵存在,否則更新

我們將使用由兩個語句的查詢:

  1. 更新,其中PRIMARY鍵值匹配(影響0或1行)。
  2. 如果PRIMARY鍵值不存在(插入1行或0行),則插入。

這是查詢:

UPDATE my_table SET 
unique_name = 'one', update_datetime = NOW() 
WHERE id = 1; 

INSERT INTO my_table 
SELECT 1, 'one', NOW() 
FROM my_table 
WHERE id = 1 
HAVING COUNT(*) = 0; 

這些查詢的只有一個會產生作用。 UPDATE很容易。至於INSERTWHERE id = 1如果id存在,則返回結果;如果不存在,則返回結果。 HAVING COUNT(*) = 0反轉,如果id是新的,則返回一行;如果它已經存在,則返回no行。

我已經探索了相同想法的其他變體,例如LEFT JOINWHERE,但它們看起來更復雜。歡迎改進。

0

我的方法如何?

假設您有一張帶有自動增量id和三個文本列的表格。您想要插入/更新column3的值,並將column1和column2中的值作爲(非唯一)鍵。

我使用此查詢(無明確鎖定表):

insert into myTable (id, col1, col2, col3) 
select tmp.id, 'col1data', 'col2data', 'col3data' from 
(select id from myTable where col1 = 'col1data' and col2 = 'col2data' union select null as id limit 1) tmp 
on duplicate key update col3 = values(col3) 

什麼不對?對我而言,它的工作方式是我想要的。