我对重复更新查询中的插入感到困惑。 我有MySQL表,结构如下:
如果我的table.person中存在id,则我想为person更新some_text和some_other_text值,否则在此表中插入新记录。如果person_id不是PRIMARY,怎么办呢?
答案 0 :(得分:9)
您需要一个查询,检查是否存在与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 (...)
答案 1 :(得分:7)
您的问题非常有效。这是一个非常常见的要求。由于MySQL提供的功能,大多数人都会弄错。
PRIMARY
密钥,否则请更新。ON DUPLICATE KEY UPDATE
PRIMARY
或任何UNIQUE
键存在,否则更新! ON DUPLICATE KEY UPDATE
可能出现什么问题?您插入了一个假定的新记录,其中包含一个新的PRIMARY
键值(例如一个UUID),但您的UNIQUE
键恰好有一个重复的值。
您想要的是一个正确的例外,表示您正在尝试将副本插入UNIQUE
列。
但你得到的是不受欢迎的UPDATE
! MySQL将采用冲突记录并开始覆盖其值。如果这种情况无意中发生,则您已经删除了旧记录,并且对旧记录的任何传入引用现在都引用了新记录。由于您可能不会告诉查询更新PRIMARY
列,因此无法找到新的UUID。如果您遇到这些数据,它可能没有任何意义,您将不知道它来自何处。
除非存在PRIMARY
密钥,否则我们需要实际 插入的解决方案,否则更新。
我们将使用包含两个语句的查询:
PRIMARY
键值匹配的位置(影响0或1行)。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
很简单。至于INSERT
:WHERE id = 1
如果id存在则会产生一行,如果不存在则不会产生行。 HAVING COUNT(*) = 0
将其反转,如果id为new,则生成一行,如果已存在则不生成行。
我已经探索了相同想法的其他变体,例如使用LEFT JOIN
和WHERE
,但它们看起来更复杂。欢迎改进。
答案 2 :(得分:5)
13.2.5.3 INSERT ... ON DUPLICATE KEY UPDATE Syntax
如果指定ON DUPLICATE KEY UPDATE,则插入一行 会在UNIQUE索引或PRIMARY KEY,MySQL中导致重复值 执行旧行的更新。
示例:
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)
答案 3 :(得分:1)
我碰到这篇文章是因为我需要标题中写的内容,并且找到了一个非常方便的解决方案,但是这里没有人提到它,因此我想到将其粘贴到这里。注意,如果要启动数据库表,此解决方案非常方便。在这种情况下,当您创建对应的表时,照常定义主键等,对于要唯一的列组合,只需添加
UNIQUE(column_name1,column_name2,...)
在CREATE TABLE
语句的末尾,对于要唯一的指定列的任何组合。像这样,根据this page here,“ MySQL会使用column_name1和column_name2列中的值组合来评估唯一性”,如果尝试插入已经具有column_name1值组合的插入,则会报告错误和column_name2您在插入中提供的。这种创建数据库表的方式与相应的INSERT ON DUPLICATE KEY
语法相结合似乎是最适合我的解决方案。在实际开始使用表格之前,只需要仔细考虑一下即可;设置数据库表时。
答案 4 :(得分: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)
有什么问题吗?对我来说,它按我想要的方式工作。
答案 5 :(得分:0)
一种灵活的解决方案应该保留INSERT ... ON DUPLICATE KEY UPDATE
提供的原子性,并且无论它是否为autocommit=true
都可以工作,并且不依赖于隔离级别为REPEATABLE READ
或更高的事务。
任何在多个语句之间执行“先检查后行动”的解决方案都不满足。
以下是选项:
INSERT INTO table (record_id, ..., some_text, some_other_text) VALUES (...);
IF <duplicate entry for primary key error>
UPDATE table SET some_text = ..., some_other_text = ... WHERE record_id = ...;
IF affected-rows = 0
-- retry from INSERT OR ignore this conflict and defer to the other session
UPDATE table SET some_text = ..., some_other_text = ... WHERE record_id = ...;
IF affected-rows = 0
INSERT INTO table (record_id, ..., some_text, some_other_text) VALUES (...);
IF <duplicate entry for primary key error>
-- retry from UPDATE OR ignore this conflict and defer to the other session
INSERT ... ON DUPLICATE KEY UPDATE
并在一条语句中完成:INSERT INTO table (record_id, ..., some_text, some_other_text) VALUES (...)
ON DUPLICATE KEY UPDATE
some_text = if(record_id = VALUES(record_id), VALUES(some_text), some_text),
some_other_text = if(record_id = VALUES(record_id), VALUES(some_other_text), some_other_text)
IF affected-rows = 0
-- handle this as a unique check constraint violation
注意:在这些示例中,affected-rows
表示受影响的行,而不是 found 行。两者可能会混淆,因为a single parameter switches which of these values the client is returned。
还请注意,如果在执行更新时未实际修改some_text
和some_other_text
(并且记录也未更改),则对affected-rows = 0
的检查将失败。>