插入MySQL表或更新(如果存在)


875

我想向数据库表中添加一行,但是如果存在具有相同唯一键的行,我想更新该行。

例如:

insert into table (id, name, age) values(1, "A", 19)

假设唯一键为id,并且在我的数据库中有一行id = 1。在这种情况下,我想用这些值更新该行。通常,这会产生错误。
如果使用insert IGNORE它,将忽略该错误,但仍不会更新。


5
对于这种用例,SQL需要一种官方语法,该语法不会强制语法中的值重复并且保留主键。
皮特·阿尔文,

要获取受影响的ID,请参阅MySQL ON DUPLICATE KEY-最后插入ID?
克里斯·瑞夫

注意:从5.7版本开始,此方法不直接支持WHERE子句作为INSERT / UPDATE操作的一部分。同样,如果对审计目的很重要,则UPDATE实际上算作两个单独的操作(DELETE和INSERT)。(Learnbit)
dreftymac

Answers:


1599

采用 INSERT ... ON DUPLICATE KEY UPDATE

查询:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name="A", age=19

86
+1从我发现的结果来看,此方法对于自动增量键和其他唯一键冲突的问题要比REPLACE INTO少,并且效率更高。
Andrew Ensley

46
我知道你们所有人都在暗示这一点,但是我想对其他人坦白。如果您插入的ID不是PRIMARY KEY或UNIQUE,则此操作将无效。最初这对我不起作用,因为我的ID并非唯一。
凯文

7
我想知道为什么成功更新(在重复键上)单行时会导致affected row count结果2?其他人有这个结果吗?(数据已正确更新:意味着仅更新了一行)
Dimitry K 2014年

14
这有点迟了,但是无论如何:手册中指出,更新会将ON DUPLICATE KEY UPDATE受影响的行增加2。如果实际未更新任何内容,则报告0(与常规相同UPDATE)。
Vatev

61
还要注意,您可以使用VALUES(名称)来引用您尝试插入的值,例如INSERT INTO表(id,名称,年龄)VALUES(1,“ A”,19)重复键更新name = VALUES(name) ,age = VALUES(age)
好奇的山姆

246

签出REPLACE

http://dev.mysql.com/doc/refman/5.0/en/replace.html

REPLACE into table (id, name, age) values(1, "A", 19)

11
@Piontek因为这一过程更短并且更容易理解,而且没有人解释为什么“插入重复数据”更好。
Mr_Chimp

77
它会更改记录的ID,因此可能会破坏外部引用。
boh 2013年

102
REPLACE INTO的另一个问题是,您必须为ALL字段指定值,否则字段将丢失或被默认值替换。如果行存在,REPLACE INTO本质上将删除该行,然后插入新行。在此示例中,如果您执行'REPLACE INTO表(id,age)值(1,19),则名称字段将为空。
Dale

33
实际上,这是删除整个行并执行新的INSERT。
mjb 2014年

8
@mjb因此,您需要具有DELETE特权,如果不需要,最好不要拥有。我的环境的数据库用户仅具有INSERT和UPDATE权限,因此REPLACE将不起作用。
ndm13 2015年

54

使用批处理插入时,请使用以下语法:

INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
    name = VALUES (name),
    ...

如果您想操纵新的值,则非常有用
Hunger

如果VALUES(name)不起作用,您可以尝试name = 'name_value',...;
Son Pham

26

试试看:

INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;

希望这可以帮助。


6
实际上,我不需要将新值添加到具有新ID的另一行中,而是想用此值替换id = 1的现有值。(据我了解,这会增加ID并添加数据)
Keshan 2010年

49
我认为他不想在重复项上将ID增加一。
唐尼2010年

7
“ transgress”不是您要查找的词:)不幸的是,现在我已经看到“
transgress

1
您是在寻找“遍历”还是“封面”?
克里斯·德夫

4
@mwfearnley您试图形象化的单词是“超越”。只花了一年半的时间:-)
迈克尔·克雷布斯

20

尝试这个:

INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'

注意:
此处,如果id是主键,则在首次插入后id='1'每次尝试插入id='1'都会更新名称和年龄,而旧名称年龄也会更改。


我想不使用id工作。您是否尝试过没有主键?
尔克斯

@ersks看到了这个问题。用户询问何时有唯一密钥
Rasel's

我明白了,但是我正在尝试解决我的问题,在这些问题中只有这些值才是已知的。因此,在这种情况下,我写了以上评论,希望能找到正确的解决方案。
尔克斯

15
INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);

INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;

REPLACE INTO table (id, name, age) VALUES(1, "A", 19);

所有这些解决方案都会对您的问题起作用。

如果您想详细了解这些声明,请访问此链接


REPLACE没有记录在链接的文档中。
雷·巴克斯特

sql输入错误,您的示例将无法正常工作。它是INSERT INTO TABLE(不是INTO TABLE)和ON DUPLICATE KEY UPDATE(不是DUPLICATE UPDATE SET)。不知道是谁在支持这一点
罗伯特·辛克莱

1
很抱歉输入错误。感谢您纠正我
Dilraj Singh

11

使用SQLite时:

REPLACE into table (id, name, age) values(1, "A", 19)

前提是这id是主键。否则,它仅插入另一行。请参阅INSERT(SQLite)。


什么replace into不正是“存在时,INSERT INTO,或更新”。@Owl
DawnSong

+1(1 of 3)我发现这种方法适用于我的情况-我需要用唯一键替换现有的行,或者如果不存在,则添加该行。这里最简单的解决方案。
therobyouknow

(3之2)我的查询:CREATE TRIGGER worklog_update AFTER INSERT ON worklog FOR EACH ROW REPLACE INTO support_time_monthly_hours ( ProjectID, monthTotalTime, Year, Month ) SELECT jiraissue.PROJECT, SUM(worklog.timeworked), YEAR(CURRENT_DATE()), MONTH(CURRENT_DATE()) FROM worklog, jiraissue WHERE worklog.issueid = jiraissue.ID AND jiraissue.PROJECT = (SELECT PROJECT FROM jiraissue WHERE NEW.issueid = jiraissue.ID ) AND worklog.startdate BETWEEN DATE_FORMAT(NOW() ,'%Y-%m-01 00:00:00') AND NOW();
therobyouknow


2
mysql中的问题是在未提供其他值的情况下replace将删除其他值。但是,@ fabiano-souza解决方案更合适
Quamber Ali

11

仅仅因为我在这里寻找该解决方案,而是要从另一个结构相同的表(在我的情况下,是将测试数据库更新为活动数据库)进行更新:

INSERT  live-db.table1
SELECT  *
FROM    test-db.table1 t
ON DUPLICATE KEY UPDATE
        ColToUpdate1 = t.ColToUpdate1,
        ColToUpdate2 = t.ColToUpdate2,
        ...

如在其他地方提到的,仅您要更新的列需要在之后包含ON DUPLICATE KEY UPDATE

无需列出INSERT或中的列SELECT,尽管我同意这可能是更好的做法。


4

如果您想将non-primary字段作为条件/条件ON DUPLICATE,则可以UNIQUE INDEX在该表上键入一个键以触发DUPLICATE

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);

而且,如果您希望合并两个字段以使其在表上唯一,则可以通过在最后一个参数上添加更多内容来实现此目的。

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);

注意,只要确保删除首先具有相同的数据nameage跨其他行值。

DELETE table FROM table AS a, table AS b WHERE a.id < b.id 
AND a.name <=> b.name AND a.age <=> b.age;

之后,它应该触发ON DUPLICATE事件。

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name = VALUES(name), age = VALUES(age)

2

在我的情况下,我在下面的查询中创建了一个查询,但是在第一个查询中是否id已经存在1并且年龄已经存在,在此之后,如果创建第一个查询,则不age包含的值age将为none

REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19

为了避免上述问题,创建如下查询

INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19

可能会帮助您...


2
有谁知道为什么我们必须两次分配值?MySQL为什么不允许我们在ON DUPLICATE KEY UPDATE处结束查询而不重复所有赋值语句?一些数据库表有很多列,这似乎是多余的/免费的。我知道为什么我们可以选择其他任务,但为什么不选择忽略它们呢?只是好奇是否有人知道。
蓝色之水

2

万一您想保留旧字段(例如:姓名)。查询将是:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name=name, age=19;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.