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');
相关推荐
long3161 天前
MySQL 学习练习(配套 01 入门资料)
数据库·学习·mysql
数据库小学妹1 天前
数据库等保三级和四级有什么区别?从访问控制到备份恢复的完整对比
数据库·安全·数据库安全·三级等保·等保合规·四级等保
看浪的路人1 天前
第3讲:手写第一个 MCP Server(Python SDK)
jvm·数据库·oracle
大眼、不聚光1 天前
5.mysql--主从同步安装部署
数据库·mysql
Yan_chen6661 天前
SQL-LABS_Less18-20实战攻略
数据库·sql·web安全·网络安全
oradh1 天前
Oracle RAC OCR维护操作总结
数据库·oracle·ocr·rac ocr维护·ocr维护·vote disk
snow@li1 天前
HikariCP:高性能数据库连接池全景深入梳理
java·数据库
一个天蝎座的程序猿1 天前
传统数据库迁金仓KES,空值问题差点让财务报表翻车
数据库
wWYy.1 天前
Mysql:索引下推
数据库·mysql
风123456789~1 天前
【Oracle专栏】ORA-02069: global_names 参数
数据库·oracle