在重复密钥更新上与插入相同
Posted
技术标签:
【中文标题】在重复密钥更新上与插入相同【英文标题】:On Duplicate Key Update same as insert 【发布时间】:2021-11-11 21:32:09 【问题描述】:我四处寻找,但没有找到是否可能。
我有这个 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)
我希望有人能告诉我最有效的方法是什么。
【问题讨论】:
AFAIK,重述每一列的方法,a=VALUES(a), b=VALUES(b), ...
是你需要走的路。这就是我对所有INSERT ON DUPLICATE KEY UPDATE
语句的处理方式。
澄清一下,只要您理解所回答的问题是:您必须重述您想要更新的每一列吗?是的,如果您想在 25 列的表中插入或更新 8 列,则必须两次声明这 8 列——一次在插入部分,一次在更新部分。 (您可以在更新部分跳过主键,但最简单的方法是预先格式化“a=1,b=2,c=3”字符串并将其嵌入两次。)但是,您不必重述剩余的 17 列你不想改变。如果它变成 UPDATE,它们将保留现有值。
我添加了该评论,因为问题和答案让我不确定带有这种 INSERT 的未提及列,然后我在确切了解要测试的内容后对其进行了测试。 INSERT ON DUPLICATE KEY UPDATE 使未提及的列在 UPDATE 上保持不变,但在 INSERT 上为它们提供默认值。使用 REPLACE INTO,未提及的列始终获取默认值,而不是现有值。
检查***.com/questions/2472229/…,我想你在找什么
【参考方案1】:
给出了UPDATE
语句,以便可以将旧字段更新为新值。如果您的旧值与新值相同,为什么还要更新它?
例如。如果您的列a
到g
已经设置为2
到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=a, b=b, c=c, d=d, e=e, f=f, g=g;
从LAST_INSERT_ID
获取id
;您需要指定您正在使用的后端应用程序。
对于 LuaSQL,conn:getlastautoid()
获取值。
【讨论】:
只是举例。在现实生活中的查询中存在差异。我的问题很简单:如果它们与插入中的相同,我真的需要指定所有字段(和我的第一个示例中的值)吗?我只想插入全部或者是否存在唯一值匹配:全部更新。 “额外说明一下,我也想使用变通方法来获取 ID!” @Tresdin,据我所知,这只是语法糖。就个人而言,我从未见过有人使用 a=VALUES(a)、b=VALUES(b) 等。也许您可以为该列分配多个值?不知道为什么你不只是事先连接数据。 如果表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
将设置 a = 10
为什么所有的赞成票?这不起作用。正如@Bùi Việt Thành 指出的那样,使用a=a
不会更新任何内容。 (原始问题中的查询实际上也没有更新任何内容,但更新似乎是 OP 的意图。)【参考方案2】:
有一个 MySQL 特定的 SQL 扩展可能是您想要的 - REPLACE INTO
但是它的工作方式与“ON DUPLICATE UPDATE”不太一样
它删除与新行冲突的旧行,然后插入新行。只要您在表上没有主键就可以了,但如果您有,那么如果任何其他表引用该主键
你不能引用旧行中的值,所以你不能做相当于
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
可能不会被您接受,因为它会删除通过唯一键发生冲突的旧行。
其他人suggested before 你可以通过这样做来减少一些打字:
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
查询之前坚持使用主键?
否 - 删除该行并创建一个新行,这将有一个新的主键。
这不会减慢处理大型数据集的过程,就像导入 100K 或更多行的 csv?
正如前面的评论提到的,REPLACE INTO 删除并重新创建该行。因此,如果您有外键,则约束将失败,因此 REPLACE INTO 将失败。您最好的方法是使用 VALUES() 进行 DUPLICATE KEY UPDATE【参考方案3】:
以下是您的问题的解决方案:
我已经尝试解决像你这样的问题,我想建议从简单的方面进行测试。
按照以下步骤:从简单的解决方案中学习。
第 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;
第 2 步:创建一个包含两列的索引,以防止使用以下 SQL 查询重复数据:
ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);
或者,从 GUI 中创建两列的索引,如下所示:
第 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';
【讨论】:
感谢您的演练 - 让我明白了,非常感谢,谢谢! 仅供参考,以纯文本形式存储密码是一个糟糕的主意,就像试图在数据库级别防止密码重复一样。【参考方案4】:没有其他办法,我必须指定所有内容两次。第一个用于插入,第二个用于更新。
【讨论】:
这是不正确的,不应该是公认的答案。 @Danack 的答案是正确的答案。 你应该重新阅读问题@WayneWorkman,这是正确的答案。【参考方案5】:如果您能够使用脚本语言来准备 SQL 查询,您可以通过使用 SET
而不是 (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;
【讨论】:
您应该转义您的值或使用您的值执行准备好的语句。 @ChinotoVokro - 我同意。这只是呈现“你怎么做”,甚至没有在示例中使用任何查询函数。 这是指定键数组和值数组的一个很好的替代方法。谢谢。【参考方案6】:您可能需要考虑使用REPLACE INTO
语法,但请注意,在重复 PRIMARY / UNIQUE 键时,它会DELETES该行并INSERTS一个新行。
您无需重新指定所有字段。但是,您应该考虑可能的性能下降(取决于您的表设计)。
注意事项:
如果您有 AUTO_INCREMENT 主键,它将被赋予一个新主键 可能需要更新索引【讨论】:
如果索引也是另一个表的外键,则违反约束,由删除部分触发。【参考方案7】:我知道已经晚了,但我希望有人能得到这个答案的帮助
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/en/library/insert-on-duplicate-key-update/
http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/
【讨论】:
好答案。但是使用 VALUES() 来引用新行是deprecated as of MySQL 8.0.20。链接中详细介绍的新方法是为行使用别名。在这个例子中,别名是new
:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
@user697473 我收到错误消息:“您的 SQL 语法有错误;请查看与您的 MariaDB 服务器版本相对应的手册,以在 'AS new ON DUPLICATE KEY 附近使用正确的语法UPDATE a = new.a'",并且在我实现这个子句之前,我的查询正在工作(显然出现重复键时除外)。有什么想法吗?
@aaron - 抱歉,没有好主意。 MySQL 8.0.20 4 月底刚刚发布;也许 MariaDB 还没有赶上。这可以解释为什么它会给你一个错误。
@user697473 是的,我实际上只是在原始答案中尝试了 a = VALUES(a) 方法,它奏效了,无论如何谢谢。
我和@aaron 有同样的麻烦,所以我有义务使用 VALUES 函数,即使知道它已被弃用。这个问题有更新吗?【参考方案8】:
你可以使用插入忽略这种情况,如果它得到重复的记录,它会忽略 插入忽略 ... ; -- 没有 ON DUPLICATE KEY
【讨论】:
我想在不存在时插入它,否则更新它。不要忽视它。 为什么要更新它,如果你用以前/相同的值更新,如果它是一个新值,那么它就可以了,如果不插入忽略它的工作 最有可能是因为值可能会更改,如果不存在,他想创建一条记录,但如果存在(有更改)则更新现有记录,而无需往返返回的开销应用程序,然后再次返回数据库。以上是关于在重复密钥更新上与插入相同的主要内容,如果未能解决你的问题,请参考以下文章
插入批处理,如果在Codeigniter 3 HMVC中有重复的密钥更新