141 lines
7.6 KiB
MySQL
141 lines
7.6 KiB
MySQL
|
|
-- =============================================================================
|
|||
|
|
-- 一次性迁移:库存状态列存量洗刷(配合 status 硬隔离上线)
|
|||
|
|
--
|
|||
|
|
-- 背景
|
|||
|
|
-- 三张库存表(stock_buy / stock_semi / stock_product)的 status 列自建表起
|
|||
|
|
-- 就是「只写不读」的:入库时写一次 '在库',此后全仓再无任何代码更新它,
|
|||
|
|
-- 分配器也从不看它。现分配器已改为「仅 status = '在库' 才可被分配」
|
|||
|
|
-- (见 app/services/inventory_reservation.py::allocatable_filter)。
|
|||
|
|
--
|
|||
|
|
-- 该过滤是 Fail-Closed 的:status 为 NULL、空串或任何非 '在库' 值的行
|
|||
|
|
-- 都会被排除在候选集之外。因此上线该过滤**之前**必须先跑本脚本,把
|
|||
|
|
-- 本该可用的存量行刷回 '在库',否则它们将集体无法出库。
|
|||
|
|
--
|
|||
|
|
-- 洗刷范围(刻意收敛,只动 status 一列)
|
|||
|
|
-- (status IS NULL OR status <> '在库')
|
|||
|
|
-- AND (stock_quantity > 0 OR available_quantity > 0)
|
|||
|
|
--
|
|||
|
|
-- · 为什么带 stock_quantity 条件,而不只是 available_quantity:
|
|||
|
|
-- 借出未还的行 available_quantity 可能为 0 而 stock_quantity > 0。
|
|||
|
|
-- 归还时 process_return() 会给该行加回 available_quantity,届时若
|
|||
|
|
-- status 仍为 NULL,这行就永远出不去了。此处一并覆盖,堵住这个未来缺口。
|
|||
|
|
--
|
|||
|
|
-- · 为什么排除「无实物的行」(stock_quantity / available_quantity 均为空或 0):
|
|||
|
|
-- 这类行多为历史脏数据(实测 stock_buy id=807 是整行全空的幽灵记录)。
|
|||
|
|
-- 把它们刷成 '在库' 等于凭空声明一批并不存在的合格库存,是有害的。
|
|||
|
|
-- 它们本来就不会被分配(分配器要求 available_quantity > 0),保持原样无害。
|
|||
|
|
-- 可用文末「0-b)」的查询把它们捞出来人工核对。
|
|||
|
|
--
|
|||
|
|
-- ● 为什么**不**刷 quality_status / inspection_status(对原始需求的偏离,务必知悉)
|
|||
|
|
-- 1) stock_buy **没有** quality_status 列,只有 inspection_status。
|
|||
|
|
-- 统一写 `SET quality_status='合格'` 会直接报 column does not exist,
|
|||
|
|
-- 脚本根本跑不完。
|
|||
|
|
-- 2) 更要命的是语义:stock_semi 有 24 行 quality_status='待检'、
|
|||
|
|
-- stock_buy 有 2042 行 inspection_status='未检'(其中 1766 行可用量 > 0)。
|
|||
|
|
-- 把它们刷成 '合格' 属于**伪造质检结论** —— 等于把从未检验过的货
|
|||
|
|
-- 当成合格品放行,与本次改造「不让坏件流出」的目的正好相反。
|
|||
|
|
-- 因此本脚本只洗 status。如确有补空需求,见文末「3) 可选:质量列补空」,
|
|||
|
|
-- 那里只补 NULL,绝不覆盖任何已有取值。
|
|||
|
|
--
|
|||
|
|
-- 幂等性
|
|||
|
|
-- WHERE 条件保证可重复执行:第二次执行影响 0 行。
|
|||
|
|
--
|
|||
|
|
-- 执行
|
|||
|
|
-- docker exec -i inventory_db psql -U test -d inventory_system < 本文件
|
|||
|
|
-- =============================================================================
|
|||
|
|
|
|||
|
|
BEGIN;
|
|||
|
|
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- 0) 执行前预览:将被洗刷的行数(建议先单独跑这一段确认无误再跑 1)
|
|||
|
|
--
|
|||
|
|
-- ★ 用 COALESCE 而非裸比较:库存表允许数量列为 NULL,而 `NULL > 0` 求值为
|
|||
|
|
-- NULL(非真),裸写会让「数量为 NULL」的脏行既不进洗刷、也不进 0-b 诊断,
|
|||
|
|
-- 在两次查询里凭空消失。COALESCE 让两个条件互为补集,口径严密。
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
SELECT 'stock_buy' AS tbl, count(*) AS will_update
|
|||
|
|
FROM stock_buy
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0)
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_semi', count(*)
|
|||
|
|
FROM stock_semi
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0)
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_product', count(*)
|
|||
|
|
FROM stock_product
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0);
|
|||
|
|
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- 0-b) 被跳过的行(无实物但状态异常,多为历史脏数据)—— 仅诊断,不修改
|
|||
|
|
-- 与 0) 严格互补,两者行数之和 = 该表 status 非「在库」的总行数
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
SELECT 'stock_buy' AS tbl, id, sku, barcode, status, stock_quantity, available_quantity
|
|||
|
|
FROM stock_buy
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND COALESCE(stock_quantity, 0) <= 0 AND COALESCE(available_quantity, 0) <= 0
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_semi', id, sku, barcode, status, stock_quantity, available_quantity
|
|||
|
|
FROM stock_semi
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND COALESCE(stock_quantity, 0) <= 0 AND COALESCE(available_quantity, 0) <= 0
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_product', id, sku, barcode, status, stock_quantity, available_quantity
|
|||
|
|
FROM stock_product
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND COALESCE(stock_quantity, 0) <= 0 AND COALESCE(available_quantity, 0) <= 0;
|
|||
|
|
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- 1) 洗刷 status(三表口径一致;stock_buy 无 quality_status,故不涉及)
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
UPDATE stock_buy
|
|||
|
|
SET status = '在库'
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0);
|
|||
|
|
|
|||
|
|
UPDATE stock_semi
|
|||
|
|
SET status = '在库'
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0);
|
|||
|
|
|
|||
|
|
UPDATE stock_product
|
|||
|
|
SET status = '在库'
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0);
|
|||
|
|
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- 2) 执行后核对:三行结果应全部为 0
|
|||
|
|
-- (统计口径与 1) 的 WHERE 完全一致,故「有实物且状态非在库」的行应为 0)
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
SELECT 'stock_buy' AS tbl, count(*) AS remaining
|
|||
|
|
FROM stock_buy
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0)
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_semi', count(*)
|
|||
|
|
FROM stock_semi
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0)
|
|||
|
|
UNION ALL
|
|||
|
|
SELECT 'stock_product', count(*)
|
|||
|
|
FROM stock_product
|
|||
|
|
WHERE (status IS NULL OR status <> '在库')
|
|||
|
|
AND (COALESCE(stock_quantity, 0) > 0 OR COALESCE(available_quantity, 0) > 0);
|
|||
|
|
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- 3) 可选:质量列补空 —— 默认**注释掉**,需要时人工确认后再放开
|
|||
|
|
--
|
|||
|
|
-- ★ 只补 NULL,不覆盖任何已有取值。
|
|||
|
|
-- '待检' / '未检' 是真实业务状态,绝不能被本脚本抹成 '合格'。
|
|||
|
|
-- 实测当前三表均无 NULL 的质量列(stock_buy.inspection_status 有 2042 行为
|
|||
|
|
-- '未检',是有值状态,不在补空范围内),故本段在当前数据上是空操作。
|
|||
|
|
-- ---------------------------------------------------------------------------
|
|||
|
|
-- UPDATE stock_semi SET quality_status = '合格' WHERE quality_status IS NULL;
|
|||
|
|
-- UPDATE stock_product SET quality_status = '合格' WHERE quality_status IS NULL;
|
|||
|
|
-- UPDATE stock_buy SET inspection_status = '未检' WHERE inspection_status IS NULL;
|
|||
|
|
-- ↑ 采购件补的是「未检」而非「合格」:默认未检验是保守且诚实的取值。
|
|||
|
|
|
|||
|
|
COMMIT;
|