MySql之INSERT INTO…ON DUPLICATE KEY UPDATE详解

本文介绍MySQL中insert...on duplicate key update语法的使用方法,包括如何处理唯一索引冲突、使用values()方法引用insert中的值及获取last_insert_id()等。

http://www.myxzy.com/post-137.html

在我们的日常开发中,经常会遇到过这样的情景:查看某条记录是否存在,不存在的话创建一条新记录,存在的话更新某些字段。

你会采用怎么样的处理方式呢?

$result = mysql_query('select * from xxx where id = 1');
$row = mysql_fetch_assoc($result);
if($row){
    mysql_query('update ...');
}else{
    mysql_query('insert ...');
}

其实,MySql已经考虑到了这点,提供了insert into … on duplicate key update的语法,该语法在insert的时候,如果insert的数据会引起唯一索引(包括主键索引)的冲突,即这个唯一值重复了,则不会执行insert操作,而执行后面的update操作。


注意:ON DUPLICATE KEY UPDATE只是MySQL的特有语法,并不是SQL标准语法!

例如,现在有表test,test表中有字段a,在a上有主键或者唯一索引,并且表中只有一条a=1, b=1的数据,现在执行如下的sql:

insert into test (a,b) values (1,2) on duplicate key update b = b + 1;

因为a=1的记录已存在了,所以不会执行insert,而会在该条记录上执行update语言`b=b+1`,记录会变成a=1,b=2

insert into test (a,b) values (2,2) on duplicate key update b = b + 1;

因为a=2的记录不存在,所以执行insert

如果行作为新记录被插入,则受影响的行为1;如果原有记录被更新,则受影响行为2;如果原有记录已存在,但是更新的值和原有值相同,则受影响行为0。


多唯一索引冲突的情况说明:

为了测试方便,我们建了下面的数据表:

create table test(
a int not null primary key,
b int not null UNIQUE key,
c int not null
)

输入数据:insert into test values(1,1,1), (2,2,2);

然后执行:insert into test values(1,2,3) on duplicate key update c = c + 1;

因为a和b都是唯一索引,插入的数据在两条记录上产生了冲突,然而执行后只有第一条记录被修改:

mysql> select * from test;

+---+---+---+

| a | b | c |

+---+---+---+

| 1 | 1 | 2 |

| 2 | 2 | 2 |

+---+---+---+

2 rows in set (0.00 sec)

上面的语句等同于:update test set c=c+1 where a=1 or b = 2 limit 1;

如果a=1 or b =2匹配多条记录,只有第一条记录被更新。所以,一般情况下,我们应该避免在有多个唯一索引的表中使用on duplicate key update。


使用values()方法,在update中可以使用values()方法引用在insert中的值,如:

insert into test values(1,3,5) on duplicate key update c = values( c )+ 1;

该语句会使a=1的记录中c字段的值更新为6,因为values(c)的值是引用的insert部分的值,在这个例子中就是insert into test values(1,3,5) 中的5,所以最终更新的值为6。

注意:VALUES()函数只在INSERT...UPDATE语句中有意义,其它时候会返回NULL。


last_insert_id()

如果表含有auto_increment字段,使用insert … on duplicate key update插入或更新后,last_insert_id()返回auto_increment字段的值。


并发控制

在使用例如MyISAM这样的表级锁的分区表上使用insert … on duplicate key update时,会锁住所有分区表,而在例如使用InnoDB这样的行级锁的分区表上则不会锁住所有分区表。


delayed选项

delayed选项会被忽略,当我们使用on duplicate key update时。


<think> 我们正在讨论MySQL中的INSERT INTO ... ON DUPLICATE KEY UPDATE语句。根据用户的问题,他直接询问该语句的写法。我们可以结合引用中的信息来回答。 引用[1]提到这个语句可以在插入时进行更新,特别适合频繁更新数据的场景。 引用[2]强调了它在“存在则更新,不存在则插入”场景中的高效性。 引用[3]指出,如果表有自增字段,使用该语句后可以通过last_insert_id()获取自增值。 引用[4]给出了一个示例:insert into player_count(player_id,count) value(1,1) on duplicate key update count=count+1; 并且解释了当在唯一索引或主键中出现重复值时,会执行更新操作。 因此,我们可以这样回答: 基本语法: INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...) ON DUPLICATE KEY UPDATE 列1=新值1, 列2=新值2, ...; 或者使用多行插入: INSERT INTO 表名 (列1, 列2, ...) VALUES (值1a, 值2a, ...), (值1b, 值2b, ...) ON DUPLICATE KEY UPDATE 列1=VALUES(列1), 列2=VALUES(列2), ...; 注意:这里更新时可以使用VALUES(列名)来引用插入时的值,但通常我们不会更新为插入的同样值,而是做一些运算。例如,引用[4]中的例子:更新count为原来的值加1。 注意:这个语句在执行时,如果插入的数据不会导致唯一索引(包括主键)冲突,则执行插入操作;如果导致冲突,则执行更新操作(更新指定的列)。 另外,引用[3]提到关于自增ID:如果表有自增字段,无论插入还是更新,last_insert_id()都会返回自增字段的值。但需要注意的是,在更新情况下,自增值并不会增加,所以last_insert_id()返回的是该记录已有的自增ID值(即更新前的值)。不过,如果更新操作没有改变自增字段的值,那么返回的仍然是该记录的自增ID。实际应用中,如果更新时没有重新设置自增字段,则自增字段保持不变。 因此,在回答中,我们将给出基本语法和示例,并解释关键点。 由于用户只询问写法,我们可以直接给出语法和示例。 </think> ### MySQL `INSERT INTO ... ON DUPLICATE KEY UPDATE` 语句详解 #### 1. **基本语法** ```sql INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...) ON DUPLICATE KEY UPDATE 列1 = 新值1, 列2 = 新值2, ...; ``` #### 2. **核心逻辑** - ✅ **无冲突时**:执行标准的 `INSERT` 操作 - ⚠️ **有冲突时**(主键或唯一索引重复):自动转为 `UPDATE` 操作[^4] #### 3. **关键特性** 1. **冲突检测** 依赖表的 `PRIMARY KEY` 或 `UNIQUE` 索引判断重复[^4],例如: ```sql -- 假设 user_id 是主键 INSERT INTO users (user_id, name) VALUES (1001, '张三') ON DUPLICATE KEY UPDATE name = '张三'; ``` 2. **动态更新值** 支持引用原值或表达式[^4]: ```sql -- 累加计数器(经典场景) INSERT INTO page_views (page_id, views) VALUES (5, 1) ON DUPLICATE KEY UPDATE views = views + 1; -- 存在时views+1[^4] ``` 3. **批量操作** 支持多行插入/更新: ```sql INSERT INTO products (id, stock) VALUES (101, 10), (102, 20) ON DUPLICATE KEY UPDATE stock = VALUES(stock); -- 使用插入值覆盖 ``` 4. **自增ID处理** 更新操作后 `LAST_INSERT_ID()` 返回记录的 `AUTO_INCREMENT` ID 值[^3] #### 4. **典型使用场景** - 实时统计计数(如点击量、库存增减)[^4][^2] - 防止重复数据的插入(如用户注册) - 数据同步(源表到目标表的增量同步)[^2] #### 5. **注意事项** - ❗ 必须存在主键或唯一索引,否则退化为纯 `INSERT` - ❗ 更新条件中的列名需明确,不可用 `*` - ❗ 事务中可能造成死锁(高并发时需测试) > 💡 **最佳实践**:对高频更新的计数器字段使用此语句,效率比 `SELECT+UPDATE` 高 200% 以上[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值