MySQL插入非PRIMARY键的重复更新

moo*_*der 8 mysql

我对重复更新查询中的插入很困惑.我有MySQL表,结构如下:

  • record_id(PRIMARY,UNIQUE)
  • person_id(UNIQUE)
  • SOME_TEXT
  • some_other_text

如果我的table.person中存在id,或者在此表中插入新记录,我想为person更新some_text和some_other_text值.如果person_id不是PRIMARY,怎么办呢?

Lor*_*nzi 12

您需要一个查询,检查是否存在与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')
Run Code Online (Sandbox Code Playgroud)

另一个更好的方法是

UPDATE table.person SET (...) WHERE person_id='SomeValue'
IF ROW_COUNT()=0
    INSERT INTO table.person (...) VALUES (...)
Run Code Online (Sandbox Code Playgroud)

  • **注意**如果您尝试使用相同的值更新帖子,则`ROW_COUNT()`将返回"0",从而导致重复的帖子. (3认同)
  • 事实证明,条件查询仅在存储过程中有效。我找到了在常规查询中有效的替代方法,并将在此处发布单独的答案。 (2认同)

Tim*_*imo 8

你的问题非常有效.这是一个非常常见的要求.由于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;
Run Code Online (Sandbox Code Playgroud)

这些查询中只有一个会产生影响.这UPDATE很容易.至于INSERT:WHERE id = 1如果id存在则产生一行,如果不存在则没有行.HAVING COUNT(*) = 0如果id是新的,则反转该行,如果已经存在则不生成行.

我已经探索了相同想法的其他变体,例如使用LEFT JOINWHERE,但它们看起来更复杂.欢迎改进.


wch*_*ito 5

13.2.5.3 INSERT ... ON DUPLICATE KEY UPDATE语法

如果指定ON DUPLICATE KEY UPDATE,并且插入的行将导致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)
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle demo