erp系统常见优化点

反例:循环里一次次改库存

退货时,订单里有几种商品,就在代码里循环几种商品,每循环一次就向数据库发一条「把某个商品的库存加回去」的更新。

复制代码
退货 3 种商品
→ 更新库存(商品 A)
→ 更新库存(商品 B)
→ 更新库存(商品 C)
→ 共 3 次数据库往返

看起来直观,但有几个问题:

  • 商品一多,数据库往返次数线性上涨
  • 同一商品若出现多行,还可能被更新多次,既慢又容易算错

正例:先汇总,再一条语句批量更新

先把「要退的商品 + 数量」在内存里整理成一张小清单(同一商品合并数量),再交给数据库 一次更新:用分支表达式按商品编号决定每一行加多少。

复制代码
退货明细
  A × 2
  B × 5
  A × 1
      ↓ 合并
清单:A→3,B→5
      ↓
一条更新:
  仓里的 A 库存 +3
  仓里的 B 库存 +5

伪代码大意:

复制代码
1. 遍历退货明细,得到 Map:商品 → 总数量
2. 转成列表:[{商品, 数量}, ...]
3. 执行一次批量 UPDATE:
   库存 = 库存 +(按商品编号分支:A 加 3,B 加 5,...)
   且只更新列表里出现的那些商品

好处

说明
更少往返 N 次更新变成 1 次,延迟和连接开销更低
语义更清晰 「这一单要回多少货」先算清,再落库,业务和写库分层
避免重复更新 同商品多行先合并,库存只加一次正确总量
更好维护 以后换批量策略(分批、消息队列)只动落库这一层

一句话:不要在循环里打数据库,先把变更清单算好,再让数据库一次按清单改到位。

代码

优化前

java 复制代码
// 退货回补库存 ------ 优化前:N 次 SQL
for (OrderLine line : refundLines) {
    if (line.getSkuCode() == null) {
        continue;
    }
    BigDecimal qty = line.getQty() != null
            ? line.getQty().abs()
            : BigDecimal.ZERO;
    if (qty.compareTo(BigDecimal.ZERO) <= 0) {
        continue;
    }
    // 每次循环打一次库
    stockService.increase(warehouseId, line.getSkuCode(), qty.intValue());
}

对应 Mapper:

xml 复制代码
<!-- 单条加库存 -->
<update id="increase">
    UPDATE warehouse_stock
    SET qty = qty + #{qty}
    WHERE warehouse_id = #{warehouseId}
      AND sku_code = #{skuCode}
</update>

优化后:聚合 + 一条批量 SQL

java 复制代码
// 退货回补库存 ------ 优化后:先聚合,再批量 1 条 SQL
Map<String, BigDecimal> qtyBySku = new HashMap<>();
for (OrderLine line : refundLines) {
    if (line.getSkuCode() == null) {
        continue;
    }
    BigDecimal qty = line.getQty() != null
            ? line.getQty().abs()
            : BigDecimal.ZERO;
    if (qty.compareTo(BigDecimal.ZERO) <= 0) {
        continue;
    }
    // 同一 SKU 合并,避免重复更新
    qtyBySku.merge(line.getSkuCode(), qty, BigDecimal::add);
}

if (!qtyBySku.isEmpty()) {
    List<StockChangeItem> items = new ArrayList<>();
    for (Map.Entry<String, BigDecimal> e : qtyBySku.entrySet()) {
        items.add(new StockChangeItem(e.getKey(), e.getValue().intValue()));
    }
    stockService.batchIncrease(warehouseId, items);
}

DTO:

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StockChangeItem {
    private String skuCode;
    private Integer qty;
}

批量 SQL(核心):

xml 复制代码
<!-- 批量加库存:一条 UPDATE + CASE WHEN -->
<update id="batchIncrease">
    UPDATE warehouse_stock
    SET qty = qty + CASE sku_code
    <foreach collection="items" item="it">
        WHEN #{it.skuCode} THEN #{it.qty}
    </foreach>
        ELSE 0
    END
    WHERE warehouse_id = #{warehouseId}
      AND sku_code IN
    <foreach collection="items" item="it" open="(" separator="," close=")">
        #{it.skuCode}
    </foreach>
</update>
sql 复制代码
UPDATE warehouse_stock
SET qty = qty + CASE sku_code
    WHEN 'SKU001' THEN 2
    WHEN 'SKU002' THEN 5
    ELSE 0
END
WHERE warehouse_id = 'WH01'
  AND sku_code IN ('SKU001', 'SKU002');
相关推荐
q5673152311 分钟前
Curl 报 CONNECT tunnel failed, response 6xx:排查思路全解
数据库·网络协议·scrapy·http·中间件·http代理
疯狂打码的少年17 分钟前
【数据库技术】两级映像与数据独立性(逻辑/物理独立性)
数据库·笔记
Elastic 中国社区官方博客20 分钟前
从告警到根因仅需 3 分钟:使用 Elastic Agent Builder 实现自动化根因分析
运维·数据库·人工智能·elasticsearch·自动化·可用性测试
惜分飞28 分钟前
obet forcecopy功能抢救硬件故障中的数据文件
数据库·oracle
TDengine (老段)1 小时前
TDengine 应用案例 — 能源与电力监控
大数据·数据库·物联网·能源·时序数据库·tdengine·涛思数据
Java小白笔记1 小时前
Java中PDF文件导出,生成链路与实现
服务器·网络·oracle
大模型码小白2 小时前
Spring AI 框架中集成 MCP 的完整指南:从服务端到客户端的全流程实践
大数据·运维·数据库·人工智能·python·sql·spring
YHL2 小时前
🐉 天龙八部 RAG 知识库实战:从零构建你的武侠 AI 助手
数据库·人工智能
lhldsg2 小时前
课程排课实战指南:从需求分析到系统落地全流程
数据库·数据挖掘·需求分析
ECT-OS-JiuHuaShan2 小时前
共轭互逆链路论,彻底打击庸俗辩证法和不可知论
数据库·人工智能·算法·机器学习·数学建模