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');
相关推荐
2501_933670793 小时前
2026秋招数据分析岗备考路线:SQL、BI、项目与面试题拆解
数据库
我要见SA姐16 小时前
告别 Copilot?Codex 本地化部署指南
运维·数据库·机器学习·oracle·回归
xcLeigh6 小时前
聊聊国产化替换:好用数据迁移工具KDMS怎么帮咱们搞定评估难
数据库·sql·数据迁移·kes·kdms
Elastic 中国社区官方博客7 小时前
列式存储并不等同于列式数据库。Columnar 模式为 Elasticsearch 带来了什么
大数据·运维·数据库·elasticsearch·搜索引擎
我要见SA姐17 小时前
用 Claude Code 重构遗留系统:从评估到落地的完整实践指南
数据库·ide·vscode·oracle·编辑器
Nturmoils9 小时前
一份 KDMS 评估报告,怎样排出迁移先后顺序
数据库
这个DBA有点耶9 小时前
异构数据集成怎么做?5 种同步方案对比 + 金融级 CDC 实战解析
数据库·oracle·架构
独泪了无痕9 小时前
SQL函数实战:GREATEST与LEAST的技巧
数据库·sql·mysql
码少女9 小时前
Linux--多路转接之select
java·服务器·数据库
梁辰兴10 小时前
软件工程:软件维护的副作用
数据库·软件工程·梁辰兴·控制方法·软件维护的副作用·副作用类型·副作用原因