LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

请不要编写“如果存在则更新否则执行插入”的SQL代码

admin
2025年3月23日 23:21 本文热度 189

以前在工作中遇到了学要编写数据同步的SQL语句,需求很简单就是同步两个表的数据,例如:某条记录存在则更新(UPDATE),否则插入(INSERT)。下面的SQL代码似乎看上去非常合理。(我曾经也是这么做的)

IF EXISTS (SELECT 1 FROM dbo.t WHERE [key] = @key)BEGIN  UPDATE dbo.t SET val = @val WHERE [key] = @key;ENDELSEBEGIN  INSERT dbo.t([key], val) VALUES(@key@val); END

这段代码单独运行不会有任何问题,但在高并发下会出现主键冲突,甚至会出现死锁。(因为这段代码是典型的条件争用,在两个事务同时运行时候,可能都会互相等待你要更新的那一行导致死锁)

如何解决问题呢?看下面的代码,直接编写UPDATE语句如果 Key存在则直接更新,如果@@ROWCOUNT影响的行数为0则代表记录不存在,那么执行INSERT操作。

BEGIN TRANSACTION;
UPDATE dbo.t WITH (UPDLOCK, SERIALIZABLE) SET val = @val WHERE [key] = @key;
IF @@ROWCOUNT = 0BEGIN  INSERT dbo.t([key], val) VALUES(@key@val);END

  • UPDLOCK用于防止语句级别的转换死锁(让另一个会话等待,而不是鼓励受害者重试)。

  • SERIALIZABLE用于防止整个事务中对基础数据的更改(确保不存在的行继续不存在)。

这段代码在并发下不会出现问题,但是因为上面两个的关键字使用,会降低并发性能。

上述解决方案显然是针对更新的情况很多的时候比较有效,但是如果大量数据都不存在,那么先执行这个UPDATE语句就浪费了很多必要的检查。针对此情形下面是解决方案,先进行INSERT的条件检查,如果Key不存在则执行INSERT操作,否则执行UPDATE。

BEGIN TRANSACTION;
INSERT dbo.t([key], val)   SELECT @key@val  WHERE NOT EXISTS  (    SELECT 1 FROM dbo.t WITH (UPDLOCK, SERIALIZABLE)      WHERE [key] = @key  );
IF @@ROWCOUNT = 0BEGIN  UPDATE dbo.t SET val = @val WHERE [key] = @key;END
COMMIT TRANSACTION;

结论

尽量使用文中的改进方法,如果更新频繁的则先进行UPDATE判断,否则执行INSERT判断。这些代码虽然会限制并发程度,但100%不会产生死锁。


阅读原文:原文链接


该文章在 2025/3/24 17:04:33 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved