实时库存余额需动态计算为“总入库量-已销售量-已占用量”,不可依赖goods表静态库存字段;须用子查询或WITH语句聚合stock_in、order_items、reservations三张表,并用COALESCE处理空值。

用子查询算出实时库存余额再筛选不足的商品
直接查 goods 表的“库存字段”不可靠——它通常是静态值,没扣减已下单未出库的量。真正不足得看「当前总入库量 - 已销售量 - 已占用量」。必须用嵌套查询动态算。
常见错误是只联 stock_in 和 orders,漏掉 order_items 中的未发货明细,或忽略 stock_adjust(手工调账)导致结果偏高。
- 外层只保留
SELECT *或关键字段,别在最外层加复杂计算,影响可读性和索引使用 - 子查询里统一用
COALESCE(SUM(...), 0),避免SUM遇空组返回NULL导致整个余额变NULL - 如果数据库是 MySQL 5.7 或更早,子查询不能直接引用外层表别名(即不支持相关子查询),得改用
JOIN方式重写
MySQL 8+ 推荐写法:用 WITH 定义中间库存视图
比多层嵌套更清晰,也方便复用和调试。重点是把「可用库存」拆成三块独立聚合:入库、销售出库、预留占用。
g.min_stock
- 必须用
COALESCE(SUM(...), 0),不能只用COALESCE(..., 0)包单个字段 - PostgreSQL 支持
FILTER子句,可替代条件JOIN,例如:SUM(oi.quantity) FILTER (WHERE oi.status != 'cancelled') - SQL Server 用户注意:
STRING_AGG或窗口函数在汇总前可能干扰分组逻辑,这类查询里尽量避免混用
性能卡在哪?三个最容易被忽视的索引点
这个查询慢,90% 不是因为写法,而是缺索引。尤其当 order_items 表超百万行时,没索引等于全表扫三次。
-
stock_in(goods_id, status)—— 加速状态过滤和分组 -
order_items(goods_id, order_id)—— 联查时避免回表;如果还按订单状态过滤,加status到联合索引最后 -
reservations(goods_id, status)—— 预留占用通常高频变动,单独建索引比走主键快得多
别依赖“商品 ID 主键自动加速”,主键只对单表精确查找有效,JOIN + GROUP BY 场景下,复合索引顺序和覆盖字段才是关键。上线前务必用 EXPLAIN 看是否走了索引,而不是只看结果对不对。










