2015-11-03 181 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) 

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