MySQL中insertOrUpdate的功能实现方式
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">底层实现</a></li><li><a href="#_label1">相似SQL</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">浅谈主键跳跃</a></li></ul><li><a href="#_label2">总结</a></li><ul class="second_class_ul"></ul></ul></div><p>insertOrUpdate在我们日常使用中比较常见,那么它是如何实现的呢,不知道大家有没有考虑过呢?</p><p>在MySQL中,可采用<code>INSERT INTO ... ON DUPLICATE KEY UPDATE</code>语句实现insertOrUpdate功能。</p>
<p>值得留意的是,在出现重复键时,会在先前索引值和当前值之间添加临时键锁,这可能导致死锁。</p>
<p>若要使用INSERT INTO … ON DUPLICATE KEY UPDATE语句,需满足以下条件:</p>
<ul><li>表必须具有主键或唯一索引;</li><li>插入的数据必须包含主键或唯一索引列;</li><li>主键或唯一索引列的值不能为NULL。</li></ul>
<p>举个例子:</p>
<p>设想有一张student表,包括id、name和age三列,其中id是主键。现在要插入一条数据,若该数据的主键已存在,则更新该数据的姓名和年龄,否则插入该数据。</p>
<div class="jb51code"><pre class="brush:sql;">INSERT INTO student (id, name, age) VALUES (1, 'Paidaxing', 20)
ON DUPLICATE KEY UPDATE name='Paidaxing', age=18;
</pre></div>
<p class="maodian"><a name="_label0"></a></p><h2>底层实现</h2>
<p>使用<code>INSERT INTO ... ON DUPLICATE KEY UPDATE</code>语句,如果数据库中已存在具有相同唯一索引或主键的记录,则更新该记录。</p>
<p>其底层原理和执行流程如下:</p>
<ul><li>检查唯一索引或主键:执行<code>INSERT INTO ... ON DUPLICATE KEY UPDATE</code>语句时,数据库首先尝试插入新行。在此过程中,数据库会检查表中是否存在与新插入行具有相同的唯一索引或主键的记录。</li><li>冲突处理:如果不存在冲突的唯一索引或主键,新行将被正常插入。如果存在冲突,即发现重复的唯一索引或主键值,数据库将不会插入新行,而是转而执行更新操作。</li><li>执行更新:在检测到唯一索引或主键的冲突后,数据库将根据<code>ON DUPLICATE KEY UPDATE</code>后面指定的列和值来更新已存在的记录。这里可以指定一个或多个列进行更新,并且可以使用VALUES函数引用原本尝试插入的值。</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>相似SQL</h2>
<p>除了INSERT INTO … ON DUPLICATE KEY UPDATE之外,还有一些类似的SQL语句,比如:</p>
<ul><li><code>REPLACE INTO</code>:如果存在唯一索引冲突,则先删除旧记录,再插入新记录。</li><li><code>INSERT IGNORE INTO</code>:如果唯一索引冲突,则忽略该条插入操作,不报错。</li></ul>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>浅谈主键跳跃</h3>
<p>在MySQL中使用INSERT ON DUPLICATE KEY UPDATE语句时,如果插入操作失败(因为主键或唯一键冲突),而执行了更新操作,确实会导致自增主键计数器增加,即使没有实际插入新记录。</p>
<p>这是因为MySQL在尝试插入新记录时,会先分配一个新的自增主键值,无论后续是插入成功还是执行更新操作,这个主键值都已经被分配并且会增加。</p>
<p>例如,假设有一个表test定义如下:</p>
<div class="jb51code"><pre class="brush:sql;">CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY KEY,
value VARCHAR(255),
UNIQUE KEY unique_value (value)
);
</pre></div>
<p>然后执行以下语句:</p>
<div class="jb51code"><pre class="brush:sql;">INSERT INTO test (value) VALUES ('a')
ON DUPLICATE KEY UPDATE value = 'a';
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025121811024790.png" /></p>
<p>再执行一次:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025121811024778.png" /></p>
<p>此时,由于value列存在唯一键约束,并且已经存在一条记录value=‘a’,所以不会插入新记录,而是会执行更新操作。但即便如此,自增主键id的计数器依然会增加。</p>
<p>然后再插入一条新的记录:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025121811024786.png" /></p>
<p>这意味着下一次插入新记录时,自增主键的值会比之前增加,即2已经被用过了,虽然没插入成功,但是新的记录就直接用3了。</p>
<p class="maodian"><a name="_label2"></a></p><h2>总结</h2>
<p>以上为个人经验,希望能给大家一个参考,也希望大家多多支持琼殿技术社区。</p>
頁:
[1]