mySQL INSERT ... ON DUPLICATE KEY column = VALUE( column ) + 1 如何工作?

Posted

技术标签:

【中文标题】mySQL INSERT ... ON DUPLICATE KEY column = VALUE( column ) + 1 如何工作?【英文标题】:How does the mySQL INSERT ... ON DUPLICATE KEY column = VALUE( column ) + 1 work? 【发布时间】:2021-03-13 18:46:13 【问题描述】:

请参阅第一个问题之后的此问题的更新部分。

我有两个表,一个用于包含 idusers_idimagepcimagename 的视频,和 视频网址 列;另一个用于包含 idusers_idfile_nameuser_file_nameusage_count 的图像strong> 列。

两个表的主键是 id 列,它们是自动递增的,但是两个 id 值彼此不相关。 images 表的 file_name 列也有一个唯一键。 usage_count 列的默认值为 1。

phpMyAdmin 显示以下有关我正在使用的 mysql 数据库的信息:

Database server
  Server:           Localhost via UNIX socket
  Server type:      MariaDB
  Server version:   5.5.52-MariaDB - MariaDB Server
  Protocol version: 10
  Server charset:   UTF-8 Unicode (utf8)

我的目标是,每当要从视频表中删除一行时,我想首先将视频表中的图像信息复制到图像表中,如果图像(特别是 file_name 列值)在 images 表中不存在,将 usage_count 设置为 1,但是,如果图像确实存在,我想增加 usage_count 减 1 并执行更新。我搜索了“mySQL INSERT or UPDATE”并想出了 INSERT ... ON DUPLCATE KEY UPDATE 变体,这似乎是我想要做的。

但是,我以前从未使用过 INSERT ... ON DUPLICATE KEY UPDATE ... 语句。查看MySQL INSERT ON DUPLICATE KEY UPDATE 和Insert into a MySQL table or update if exists 页面,我想到了以下内容;不幸的是,我现在可以连接到我的数据库,所以我无法确定它是否正确:

INSERT
  INTO `photos`
        ( `users_id`, `file_name`, `user_file_name` )
SELECT `users_id`, `image`, `pcimagename`
FROM `videos`
WHERE `status` = \'Deleted\'
   AND `last_updated_on` <= DATE_SUB( NOW(), INTERVAL 1 DAY )
   AND `tracks_id` = 0
ON DUPLICATE KEY UPDATE usage_count = VALUES( usage_count ) + 1;

我的问题是:

    语法是否正确,具体来说:

    1a) ON DUPLICATE 短语的位置是否正确?

    1b) 我是否需要将 SELECT 语句括在括号中,我已经看到了可以使用的示例和其他没有的示例。

    UPDATE usage_count = VALUES(usage_count) + 1 短语是否会导致插入图像表的每一行在该行的预插入 usage_count 值上加 1,或者所有来自的行SELECT FROM videos 的值是否相同?我对 ON DUPLICAT 语句的这一部分实际上是如何工作的了解不多。大多数示例只是分配了一个固定值。

更新

在使用 Anina 提供的 dbFiddle 时,我注意到运行以下 INSERT 语句并没有像我预期的那样增加:

After several INSERT ... ON DUPLICATE KEY UPDATE ... statements the test 
table contains:

id  val test    action  comment
1   1   2       update  
2   2   2       update  
3   3   1       insert  

Perform another INSERT ... ON DUPLICATE KEY UPDATE ... statement,
this time to increment val 1, expecting that the test column should increment to 3.

INSERT INTO test (val ) 
VALUES ( 1 )
ON DUPLICATE KEY UPDATE test = VALUES( test ) + 1,
                        action = 'update',
                        comment = 'Why isn''t test 3?';

 However ....

 id val test    action  comment
 1  1   2       update  Why isn't test 3?
 2  2   2       update  
 3  3   1       insert  

表的创建如下:

CREATE TABLE test ( id      INT NOT NULL AUTO_INCREMENT, PRIMARY KEY( ID ),
                    val     INT NOT NULL, UNIQUE( val ),
                    test    INT DEFAULT 1,
                    action  VARCHAR( 10 ) DEFAULT 'insert',
                    comment VARCHAR( 30 ) );

谢谢

【问题讨论】:

ON DUPLICATE KEY UPDATE photos set usage_count = usage_count + 1; 手册页很清楚see the manual UPDATE usage_count = VALUES( usage_count ) + 1 短语是否会导致插入图像表的每一行在行的预插入 usage_count 值上加 1,或者所有来自 @987654333 的行@ 被赋予相同的值? 既不是第一个也不是第二个。仔细阅读关于 VALUES() 函数操作的参考手册。 fiddle - 调查。 谢谢大家。 Anina,你的小提琴对我关于 ON DUPICATE KEY 阶段的 VALUES() 表达式如何工作的问题很有帮助。 RiggsFolly,我没有为 INSERT ... ON DUPLICATE KEY UPDATE 语句打开 MySQL 5.6 参考手册页面,因为对于 v5.6,手册页中该页面的最早版本,以及我正在使用的 mySQL 版本的一个过去版本,它没有回答我所有的问题,或者如果回答了,我觉得它不够清楚,所以我错过了我在发布的问题中提出的问题,这里。不是说明书的错。这就是为什么我在这里问我的问题。但是,感谢您为阅读此问题并希望了解有关该条款的任何人添加链接。 【参考方案1】:

更新答案

为了使测试列超过 2,我在创建测试表之后添加了以下 UPDATE TRIGGER,在对现有行进行任何插入之前:

CREATE TRIGGER before_test_update
BEFORE UPDATE ON test FOR EACH ROW
BEGIN
  SET new.test = old.test + 1;
END;

这会导致测试列将其在现有行中的值增加 1,而不是失败插入的测试值 (1) 增加 1(始终为 2)。

请参阅此dbLink 以获取说明其工作原理的示例。

但是,因为 ON DUPLICATE KEY 短语只是将 INSERT 转换为 UPDATE,实习生使用上述触发器来更正测试增量。当现有行中的任何列被更新时,实际的 UPDATE 也会调用相同的触发器,从而导致测试列值不正确。

mySQL 手册说:

With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the
row is inserted as a new row, 2 if an existing row is updated, and 0 if an
existing row is set to its current values. If you specify the
CLIENT_FOUND_ROWS flag to the mysql_real_connect() C API function when
connecting to mysqld, the affected-rows value is 1 (not 0) if an existing
row is set to its current values.

ON DUPLICARE KEY UPDATE 短语转换 INSERT 时,有没有办法使用 affected-rows 值为 2 的事实到 BEFORE UPDATE TRIGGERAFTER UPDATE TRIGGER 中的 UPDATE 或其他方式来确定发生这种情况,而不是直接在 >UPDATE,其中 affected-rows 值为 1?

如果是这样,我如何从 TRIGGER 中获取 affected-rows 值?

注意:在我的代码中,我使用了来自子SELECTINSERT,因此可以在将值返回到我的程序之前添加多行。因此,我正在寻找一种在插入多行时运行的 Purely-mySQL 解决方案。

【讨论】:

您收到的帮助均未表明您需要添加触发器。你为什么这样做?您需要做的就是将您的更新声明更改为ON DUPLICATE KEY UPDATE usage_count = usage_count + 1; 感谢您的建议 gview,但我已经在使用该短语了。 BEFORE TRIGGER 是一种需要的解决方法,因为 usage_count/test 列值停止增加超过 2。 如果您确实在我的问题的更新部分查看了指向 dbFiddle 的链接,您会看到在最初添加到表后针对值 1 的插入都具有测试值 2 .我需要的是,如初始问题所述,测试值从 1 开始,使用该列的默认值,然后每次插入,通过 ON DUPLICATE ... 短语更改为 UPDAT,递增 1 ,从 1 到 2、2 到 3、3 到 4 等等。如果没有 BEFORE INSERT 触发器,就不会发生这种情况。我的答案中的 dbLink 通过添加 BEFORE TRIGGER 解决了这个问题。 但是,由于 ON DUPLICATE ... 短语仅将 INSERT 转换为 UPDATE 并不能完全起作用,因为当我更改任何列的值时,除了使用显式 UPDATE 语句进行测试时,触发器触发并且usage_count(或测试)列的值增加。在我的项目代码中,该列是usage_count。在 dbFiddle 示例中,测试列执行相同的功能。当 ON DUPLICATE 从 INSERT 切换到 UPDATE 时,我需要以某种方式只调用 BEFORE UPDATE TRIGGER。

以上是关于mySQL INSERT ... ON DUPLICATE KEY column = VALUE( column ) + 1 如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

Java上的MySQL“INSERT ... ON DUPLICATE KEY UPDATE”:如何区分插入/更新/无变化状态

MySQL中的`REPLACE`和`INSERT ... ON DUPLICATE KEY UPDATE`之间有啥实际区别?

MySQL--INSERT INTO ... ON DUPLICATE KEY UPDATE ...

mysql INSERT ... ON DUPLICATE KEY UPDATE语句

mysql INSERT ... ON DUPLICATE KEY UPDATE语句

MySQL INSERT 与 ON DUPLICATE SELECT 可能吗?