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

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 语句,以便可以将旧字段更新为新值。如果您的旧值与新值相同,为什么还要更新它?

例如。如果您的列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;

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。链接中详细介绍的新方法是为行使用别名。在这个例子中,别名是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 我收到错误消息:“您的 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

【讨论】:

我想在不存在时插入它,否则更新它。不要忽视它。 为什么要更新它,如果你用以前/相同的值更新,如果它是一个新值,那么它就可以了,如果不插入忽略它的工作 最有可能是因为值可能会更改,如果不存在,他想创建一条记录,但如果存在(有更改)则更新现有记录,而无需往返返回的开销应用程序,然后再次返回数据库。

以上是关于在重复密钥更新上与插入相同的主要内容,如果未能解决你的问题,请参考以下文章

nodejs mysql批量插入重复密钥更新

插入批处理,如果在Codeigniter 3 HMVC中有重复的密钥更新

php Yii2批量插入助手。支持“忽略”和“重复密钥更新”策略

如果不存在Mysql,则插入[重复]

在插入新行之前删除行[重复]

SQLite 插入 - 重复键更新 (UPSERT)