在重复密钥更新上与插入相同


158

我到处搜寻,但没有找到可能。

我有这个MySQL查询:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

字段ID具有“唯一索引”,因此不能有两个。现在,如果数据库中已经存在相同的ID,我想对其进行更新。但是我真的必须再次指定所有这些字段,例如:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

要么:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

我已经在插入中指定了所有内容...

需要特别说明的是,我想通过变通的方式获取ID!

id=LAST_INSERT_ID(id)

我希望有人能告诉我最有效的方法是什么。


9
重行每一列的方法AFAIK a=VALUES(a), b=VALUES(b), ...是您需要采取的方法。这就是我对所有INSERT ON DUPLICATE KEY UPDATE语句所做的方式。
Valdogg21

4
为了澄清起见,只要您理解所要回答的问题是,此处说明的所有内容都是正确的:是否必须重述要更新的每一列?是的,如果要在25列的表中插入或更新8列,则必须两次声明这8列-一次在插入部分,一次在更新部分。(您可以跳过更新部分中的主键,但是最简单的方法是预格式化“ a = 1,b = 2,c = 3”字符串并将其嵌入两次。)但是,您不必重新声明其余的17列你不想改变。如果它成为UPDATE,则他们将保留其现有值。
Timberline

3
我添加该评论是因为这些问题和答案使我不确定使用这种INSERT的未提及的列,然后在确切了解要测试的内容之后对其进行了测试。INSERT ON DUPLICATE KEY UPDATE使未提及的列在UPDATE上保持不变,但在INSERT上为它们提供默认值。使用REPLACE INTO时,未提及的列始终获得默认值,而不是现有值。
Timberline

1
检查stackoverflow.com/questions/2472229/…,我认为您正在寻找的是
Chococroc

Answers:


82

UPDATE给出该语句是为了使较旧的字段可以更新为新值。如果您的旧值与新值相同,为什么无论如何都需要更新它?

例如。如果你的列ag已经被设置为28; 无需重新更新。

或者,您可以使用:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

要获得idLAST_INSERT_ID; 您需要指定用于该应用程序的后端应用程序。

对于LuaSQL,将conn:getlastautoid()获取值。


6
仅用于示例。在现实生活中,查询存在差异。我的问题很简单:我是否真的需要指定所有字段(以及第一个示例中的值),如果它们与插入字段中的相同?我只想插入全部,或者如果有唯一的值匹配项:全部更新。
罗伊

1
“特别说明,我也想利用工作来获得ID!”
Agamemnus

1
@Tresdin,据我所知这只是语法糖。就个人而言,我从未见过有人使用a = VALUES(a),b = VALUES(b)等。也许您可以为该列分配多个值?不知道为什么您不只是预先连接数据。
DuckPuncher

54
如果表tbl已经具有行(1,10),则:INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)将设置a = 2。而INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=a将设置= 10
BUI越青

2
为什么要全部投票?这不起作用。正如@BùiViệtThành所指出的,使用a=a更新什么也没有。(原始问题中的查询实际上也没有更新任何内容,但是更新似乎是OP的意图。)
Roger Dueck

41

您可能想要使用MySQL的特定于SQL的扩展- REPLACE INTO

但是,它的功能与“ ON DUPLICATE UPDATE”不同

  1. 它将删除与新行冲突的旧行,然后插入新行。只要您在表上没有主键就可以了,但是如果您这样做,那么是否有其他表引用该主键

  2. 您无法引用旧行中的值,因此不能等效于

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

我想利用变通办法获得ID!

那应该可以工作—只要您的主键是自动递增的,last_insert_id()应该具有正确的值。

但是,正如我说的那样,如果您实际上在其他表中使用了该主键,则REPLACE INTO可能不会被您接受,因为它会删除通过唯一键冲突的旧行。

有人建议您在通过以下操作减少键入之前

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);

因此,replace into查询前我不能坚持使用主键吗?
罗伊

否-删除该行并创建一个新行,它将具有一个新的主键。
Danack

这会不会减慢大型数据集上的处理速度,就像导入具有100K或更多行的csv一样?
穆罕默德·奥默·阿斯兰

24

没有其他方法,我必须两次指定所有内容。首先是插入,第二是更新。


9
这是不正确的,不应被接受。@Danack的答案是正确的答案。
韦恩·沃克曼

2
您应该重新阅读问题@WayneWorkman,这是正确的答案。
罗伊,

24

这是您的问题的解决方案:

我已经尝试解决像您这样的问题,并且我建议从简单的方面进行测试。

请按照下列步骤操作:从简单的解决方案中学习。

步骤1:使用此SQL查询创建表架构

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

具有数据的表<code> user </ code>

步骤2:使用以下SQL查询创建两列索引以防止重复数据:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

或者,通过GUI创建两列索引,如下所示: 从GUI创建索引 选择列以创建索引 表<code> user </ code>的索引

步骤3:更新(如果存在),如果不使用以下查询,则插入

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

在上面的查询中运行后,表<code> user </ code>


1
谢谢您的演练-让我理解了,非常感谢,谢谢!
Michael Nilsson

仅供参考,以纯文本格式存储密码是一个糟糕的主意,正试图防止在数据库级别重复密码。
OrangeDog

10

万一您能够使用脚本语言来准备SQL查询,则可以通过使用SET而不是来重用field = value对(a,b,c) VALUES(a,b,c)

PHP的示例:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

表格示例:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

4
您应该转义您的值或使用您的值执行准备好的语句。
Chinoto Vokro

1
@ChinotoVokro-我同意。这只是呈现“如何做”,甚至在示例中都没有使用任何查询功能。
克里斯(Chris

1
这是指定键数组和值数组的好选择。谢谢。
马克·卡彭特(Marc Carpenter Jr)

5

您可能要考虑使用REPLACE INTO语法,但是要注意,在重复的PRIMARY / UNIQUE键上,它会删除行和INSERTS一个新的。

您无需重新指定所有字段。但是,您应该考虑可能的性能降低(取决于表设计)。

注意事项:

  • 如果您有AUTO_INCREMENT主键,它将被赋予一个新的主键
  • 索引可能需要更新

如果索引也是另一个表的外键,则存在约束冲突,它是由删除部分触发的。
user3290180's

4

我知道已经晚了,但我希望有人会对此答案有所帮助

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

您可以在此处阅读以下教程:

https://mariadb.com/kb/zh_CN/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/


好答案。但是自MySQL 8.0.20起,不推荐使用VALUES()来引用新行。在链接上详细介绍的新方法是为行使用别名。在此示例中,别名为newINSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473

@ user697473我收到错误消息:“您的SQL语法有错误;请检查与您的MariaDB服务器版本相对应的手册,以获取在'AS new ON DUPLICATE KEY UPDATE a = new.a'附近使用的正确语法。” ,并且在实现此子句之前,我的查询已经正常工作(显然,当出现重复键时除外)。有任何想法吗?
亚伦

@aaron-对不起,没有好主意。MySQL 8.0.20刚刚于4月底发布;也许MariaDB尚未赶上。那可以解释为什么它给你一个错误。
user697473

@ user697473是的,实际上我只是在原始答案中尝试了a = VALUES(a)方法,它确实有效,谢谢。
亚伦

-7

您可以在这种情况下使用插入忽略,如果它得到重复的记录,它将忽略INSERT IGNORE ...; -不带ON重复键


我想在不存在时插入它,否则进行更新。不要忽略它。
罗伊

为什么要更新它,如果要使用以前/相同的值进行更新,如果它是一个新值,那么就可以了,如果不插入,请忽略它
pitu

2
最有可能是因为值可能会更改,并且他想创建一条记录(如果一条记录不存在),但是想要更新一条现有记录(如果有更改)(而没有更改),而又没有往返于应用程序然后再返回数据库的开销再次。
mopsyd
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.