当前位置: 首页 > news >正文

千万级数据批量更新优化:UPDATE替换MERGE INTO

千万级数据批量更新优化:从慢到快,UPDATE 替换 MERGE INTO 实战

大家好,我是政乾,好久没更新博客了~

今天给大家带来一篇超实用的SQL 优化实战—— 针对业务中常见的多列关联批量更新场景,
手把手教你把低效UPDATE改成高性能MERGE INTO,新手也能看懂、直接复制复用

实际开发中
-------我们经常遇到这类需求:遇到需要根据“子表”的聚合结果来更新“主表”的场景

  • 比如:统计部门下员工的指标总和,更新到部门表;
  • 统计订单商品的金额总和,更新到订单主表……

今天就以「更新妖灵之地部门的统计指标」为例,拆解原 UPDATE 写法的坑,详解MERGE INTO 优化方案,让你的批量更新效率直接起飞!


整体概览

文章目录

  • 千万级数据批量更新优化:从慢到快,UPDATE 替换 MERGE INTO 实战
    • 一、业务场景说明
    • 二、优化前的 SQL 深度剖析(它到底慢在哪?)
      • 核心问题:执行逻辑太坑(新手重点理解)
      • 性能瓶颈 & 潜在风险
    • 三、优化后:MERGE INTO 语句详解(新手逐行看懂)
      • 核心逻辑拆解(新手必读!)
        • 第一层:数据源构建(`USING (SELECT...) AS src`)
        • 第二层:匹配条件(ON)
        • 第三层:更新动作(WHEN MATCHED THEN UPDATE)
    • 四、核心:为什么 MERGE INTO 完胜原 UPDATE?
      • 底层优化逻辑:
    • 五、使用注意事项 & 拓展建议
      • 1. MERGE INTO 使用规范(新手必记)
      • 2.不同数据库写法差异
      • 3.额外优化建议
    • 六、完结撒花!!
    • 哈哈哈,如果感觉"有帮助",欢迎点赞,关注收藏哦!!!

一、业务场景说明

先明确我们的业务需求,简单直白:

  1. 有两张表:departments(部门表)、employees(员工表);
  2. 需要筛选出部门名称包含「妖灵之地]的部门;
  3. 统计这些部门下所有员工的num3总和、num4总和(就是聚合结果);
  4. 将统计结果批量更新到部门表的num1num2字段。

这是非常典型的主表 + 子表聚合关联批量更新,也是最容易写出低效SQL的场景之一!


二、优化前的 SQL 深度剖析(它到底慢在哪?)

先看优化前的原始SQL

很多新手(甚至有基础的开发者)都这么写(包括我自己刚入行那会儿也是哈哈哈):

-- 优化前:低效关联更新UPDATEdepartments t1SET(t1.num1,t1.num2)=(SELECTsum(t2.num3)asnum1,sum(t2.num4)asnum2FROMemployees t2wheret1.dept_id=t2.dept_id)wheret1.dept_namelike'妖灵之地';

核心问题:执行逻辑太坑(新手重点理解)

这条SQL看似简单,执行逻辑完全是「灾难级」,我给大家拆解底层执行流程

扫描主表–>逐行触发子查询–>数据汇总与回写

  1. 先筛选部门表(扫描主表):数据库首先根据WHERE t1.dept_name LIKE '妖灵之地',扫描departments表,筛选出符合条件的所有行(假设有100W行)。
  2. 逐行触发子查询:
    • 逐行循环100W条部门数据,每一行都执行一次子查询SELECT SUM... WHERE t1.dept_id = t2.dept_id
    • 每执行一次子查询,都要去员工表employees做全表 / 索引扫描,计算 SUM;
  3. 数据汇总与回写:子查询返回该部门的员工汇总数据,然后数据库将这行数据更新到主表。最终:100W条部门数据 → 执行100W次子查询 → 扫描100W次员工表

性能瓶颈 & 潜在风险

  1. 性能极差:“逐行”扫描的致命伤;员工表数据量越大(比如百万、千万级),执行越慢,直接拖垮数据库;
  2. 锁表风险:更新耗时过长,会长时间锁定部门表,导致其他业务无法操作,引发生产故障;
  3. 空值问题:如果部门没有员工,子查询返回 NULL,会把num1/num2更新为 NULL,业务数据出错;
  4. 扩展性差:后续加统计字段、加筛选条件,代码会越来越乱,可读性极差。

💡 新手提示:这种 **「相关子查询 + 逐行更新」的写法,是批量更新的大忌 **!数据量小的时候没感觉,数据量大直接卡死!


三、优化后:MERGE INTO 语句详解(新手逐行看懂)

针对上面的问题,我们用MERGE INTO优化,

这是Oracle迁到GaussDB等国产数据库的批量关联更新神器!

我们来先看最终优化语句:

-- 优化后:高性能 MERGE INTO 批量更新mergeintodepartments tusing(-- 第一步:先计算出需要更新的 部门ID + 统计值(只执行1次)selectdept_id,sum(s2.num3)sumnum1,sum(s2.num4)sumnum2from(-- 子查询 S1:先找出所有符合条件的部门ID-- 筛选目标部门:妖灵之地(先缩小数据范围)selectdept_idfromdepartmentswheredept_namelike'妖灵之地'groupbydept_id)s1-- 左连接员工表:避免无员工的部门被更新为NULLleftjoinemployees s2ons2.dept_id=s1.dept_idgroupbys1.dept_id)src-- 把统计结果作为「数据源」on(t.dept_id=src.dept_id)-- 关联条件:部门ID匹配whenmatchedthen-- 匹配成功就执行更新updatesett.num1=src.sumnum1,t.num2=src.sumnum2;

核心逻辑拆解(新手必读!)

看起来复杂,其实就是纸老虎!! 我们把它拆成三层结构来看:

一定要看哦!也可以自己模拟下,相信我,绝对收获满满!!

第一层:数据源构建(USING (SELECT...) AS src

这是整个优化的灵魂。我们将原本“逐行计算”的逻辑,转变为了“一次性构建一个虚拟结果集”。

  • 子查询 S1SELECT dept_id FROM departments WHERE dept_name LIKE '妖灵之地' GROUP BY dept_id

    • 这一步先把所有符合条件的部门 ID一次性取出来。即使有重复dept_id,我们用GROUP BY去重
    • 新手提示:这里先做过滤,避免了后续关联庞大departments全表。
  • 主查询LEFT JOIN employees s2 ON s2.dept_id = s1.dept_id

    • 拿着刚才过滤好的少量部门 ID(比如 1000 个),去左连接employees表。
    • 补充:为什么LEFT JOIN不是INNER JOIN
      • 因为如果某个部门没有员工(右表无匹配),LEFT JOIN会返回该部门 ID,对应的sumnum1sumnum2NULL
      • 但后续聚合SUM(NULL)结果是NULL,我们再通过后续处理或NVL转为 0,可以避免将原有数据覆盖为NULL。(这个操作看你更改的原语句情况而定,结合业务)
  • 最终聚合GROUP BY s1.dept_id并计算SUM

    • 这一步确保每个部门只返回一行汇总数据。

结论:整个USING部分,只执行了一次,就生成了所有需要更新的部门及其汇总数据。

第二层:匹配条件(ON)
ON(t.dept_id=src.dept_id)

告诉数据库:当departments表的dept_id与源数据src中的dept_id相等时,执行更新操作。

第三层:更新动作(WHEN MATCHED THEN UPDATE)
WHENMATCHEDTHENUPDATESETt.num1=src.sumnum1,t.num2=src.sumnum2;

一旦匹配上,就用源数据中的汇总值更新目标表的字段。


四、核心:为什么 MERGE INTO 完胜原 UPDATE?

给大家做直观对比,看完就知道优化的价值:

对比维度原始 UPDATEMERGE INTO
执行次数子查询执行 N 次(N = 部门数)数据源只执行 1 次
表扫描次数扫描员工表 N 次扫描部门表 + 员工表 各 1 次
空值风险无员工会更新为 NULL通过LEFT JOIN+SUM,无数据时结果为 NULL,
可通过NVL(src.sumnum1, 0)灵活处理为 0,避免数据丢失。
执行效率数据量大极慢百万级数据秒级完成(重点!!)
锁表时间长,易故障短,业务无影响
可读性差,嵌套难维护清晰,分层逻辑,易扩展

底层优化逻辑:

  • 原始 UPDATE行级循环更新(嵌套循环,时间复杂度 O (N));
  • MERGE INTO集合级批量更新(先算好所有结果,再一次性更新,时间复杂度 O (1))。

📌补充:MERGE INTO本质是「有则更新、无则插入」,这里我们只用了更新功能,完全适配批量关联更新场景,是数据库官方推荐的优化方案!(本人亲自生产实操!!)


五、使用注意事项 & 拓展建议

1. MERGE INTO 使用规范(新手必记)

  1. 关联条件必须唯一:用主键 / 唯一键(如dept_id),避免一对多导致更新异常;
  2. 必须加筛选条件:在using子查询中先缩小数据范围,不要全表更新;
  3. 优先左连接:避免子表无数据时,主表字段被更新为 NULL;
  4. 语法检查:关键字别写错(matchedupdate set缺一不可)。

2.不同数据库写法差异

  1. Oracle:原生支持 MERGE INTO,语法直接用;
  2. MySQL 8.0+:支持MERGE INTO,低版本 MySQL 可用UPDATE JOIN替代;
  3. SQL Server:语法和 Oracle 几乎一致,直接兼容。

3.额外优化建议

  1. 给关联字段加索引:dept_id(部门表、员工表都要加);
  2. 避免like '%妖灵之地%'前置百分号,会导致索引失效,改成like '妖灵之地%'
  3. 大批量更新时,分批次执行,避免一次性锁表。

六、完结撒花!!

好了,如果你已经坚持阅读了这里,那么恭喜你!

你已经掌握了一个从“看似简单”的UPDATE到“性能为王”的MERGE INTO优化之路

(快去和你小伙伴们装杯吧,哈哈哈!)

其实最后我还是想和大家说,不管我们做什么优化,抓住核心思路能一次做完的事情,绝不做 N 次。这是我们SQL优化中的黄金法则!!!

所以,当你下次UPDATE遇到子查询时,不妨多想一想:

  • 这个子查询会被执行几次?—>如果答案很多次,那本文实战的MERGE INTO就是你最好的武器!!!

哈哈哈,如果感觉"有帮助",欢迎点赞,关注收藏哦!!!

半山腰很挤,道友~咱们顶峰见!!!

http://www.jsqmd.com/news/520828/

相关文章:

  • Qwen3-ForcedAligner-0.6BGPU算力优化:梯度检查点+FlashAttention内存节省技巧
  • 嵌入式网络丢包故障的分层诊断与工程实践
  • 卡证检测矫正模型效果深度评测:对比传统OCR与深度学习方案
  • CLAP音频分类可演进:支持LoRA微调接口,兼顾零样本与领域适配
  • 基于单片机的温控风扇设计与实现
  • 终极指南:3分钟学会抖音无水印视频批量下载
  • 【收藏】500+ AI工具导航,这一站搞定你的AI工具箱!
  • NLP新手必看:如何用NLTK快速玩转语料库(附实战代码)
  • 牛客周赛Round136总结
  • 基于单片机智能水表水流量计流量设计
  • VM16安装CentOS7避坑指南:从镜像下载到快照备份的全流程详解
  • RTL8720硬件RTC中断库:高确定性时间触发方案
  • Java八股文新解:从JVM内存模型看AI模型服务的资源管理与优化
  • Llama-3.2V-11B-cot 与 Java 八股文知识库结合:构建动态更新的面试学习系统
  • 基于LDA模型的电商评论主题挖掘与情感优化策略
  • BEV与BEVFusion在自动驾驶中的核心作用及学习路径解析
  • Citra模拟器架构深度解析:高性能3DS游戏仿真技术实现
  • GLM-OCR实战:快速部署并识别复杂文档中的文字与表格
  • STM32启动流程详解:从复位向量到main函数执行链
  • Z-Image-GGUF效果展示:‘professional photography’风格与‘digital art’风格对比
  • 61:《死亡笔记》从展示处决到文化病毒:神性传播的SIR传染病模型
  • Qwen3-VL-8B快速上手教程:无需代码基础,轻松玩转多模态AI
  • 实时通信系统实战:SpringBoot整合WebSocket打造股票行情与多人聊天平台
  • KART-RERANK数据库优化实战:MySQL查询语句与文档相关性匹配
  • ️ Python SQLite数据库完全指南:从零基础到实战操作
  • 图像增强技术全解析:基于Real-ESRGAN-ncnn-vulkan的超分辨率解决方案
  • 第一次web开发前端作业
  • 解密LeRobot ACT中的Transformer架构:如何用多模态融合提升机器人动作预测精度
  • 航模新手必看:PWM、PPM、SBUS、DSM2接收机协议全解析(含实战接线图)
  • CAM++应用场景解析:如何用声纹识别技术解决会议录音分类问题