-- ============================================================================ -- 费用科目两级化改造脚本(费用统计分析报表前置主数据改造) -- 日期 : 2026-08-17 -- 数据库 : 达梦 DM8(Oracle 兼容语法) -- 目标库 : 测试库 192.168.0.49:5236(NGWL_TEST_SYSTEM / NGWL_TEST_BMS,生产执行前替换 schema) -- 背景 : -- 现有费用科目全部为平级一级科目(恒温仓仓租/水费/运费...),费用统计分析报表 -- 需要"一级科目(费用类别) - 二级科目(具体费用项)"两级结构(模板:仓储收入/散租...)。 -- 运行时解析逻辑(BusinessDocumentApplicationService.getSubInfoByCode2 分支)完全由 -- EXPENSE_ACCOUNT 的 first_subject_flag + upper_subject_code 驱动, -- 因此只需重挂主数据树 + 刷历史冗余字段,业务代码零改动。 -- -- ⚠️ 执行顺序严格按步骤,每步执行后核对验证输出再继续。 -- ⚠️ 生产执行前必须:1)已完成测试库验证 2)停业务推送或低峰期执行 3)先执行步骤0备份 -- ============================================================================ -- ============================================================================ -- 步骤 0.1 盘点现状(只读,执行后人工核对科目清单与 TOP_ORGANIZATION_ID) -- ============================================================================ SELECT SUBJECT_CODE, SUBJECT_NAME, FIRST_SUBJECT_FLAG, UPPER_SUBJECT_CODE, UPPER_SUBJECT, STATUS, ACCOUNT_EXPENSE_TYPE, TOP_ORGANIZATION_ID, SHOW_SEQ FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT ORDER BY FIRST_SUBJECT_FLAG, SUBJECT_CODE; -- 记下 TOP_ORGANIZATION_ID(下文新增一级科目要用同一个值,记为 :ORG_ID,示例按 2827) -- 记下现有科目里最大的 SHOW_SEQ(新一级科目排序用) -- ============================================================================ -- 步骤 0.2 受影响行数盘点(只读;6 张表统一四列模式) -- 条件 = FIRST_SUBJECT_CODE 命中映射表旧编码 且 SECOND_SUBJECT_CODE 为空 -- ============================================================================ SELECT 'BILLING_STATEMENT' AS TBL, COUNT(*) AS CNT FROM NGWL_TEST_BMS.BILLING_STATEMENT BS WHERE BS.SECOND_SUBJECT_CODE IS NULL AND BS.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') UNION ALL SELECT 'BUSINESS_DOCUMENT', COUNT(*) FROM NGWL_TEST_BMS.BUSINESS_DOCUMENT BD WHERE BD.SECOND_SUBJECT_CODE IS NULL AND BD.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') UNION ALL SELECT 'BILL_DETAIL', COUNT(*) FROM NGWL_TEST_BMS.BILL_DETAIL BD WHERE BD.SECOND_SUBJECT_CODE IS NULL AND BD.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') UNION ALL SELECT 'BILLING_RULES', COUNT(*) FROM NGWL_TEST_BMS.BILLING_RULES BR WHERE BR.SECOND_SUBJECT_CODE IS NULL AND BR.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') UNION ALL SELECT 'TASK_FLOW_RECORDS', COUNT(*) FROM NGWL_TEST_BMS.TASK_FLOW_RECORDS TF WHERE TF.SECOND_SUBJECT_CODE IS NULL AND TF.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') UNION ALL SELECT 'CONTRACT_MANAGE_DETAIL', COUNT(*) FROM NGWL_TEST_SYSTEM.CONTRACT_MANAGE_DETAIL CD WHERE CD.SECOND_SUBJECT_CODE IS NULL AND CD.FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006'); -- ============================================================================ -- 步骤 0.3 重复科目 TYLSCCSNCZ 引用检查(只读;决定后续是"仅重挂"还是"迁移引用后停用") -- ============================================================================ SELECT 'LEASE' AS SRC, COUNT(*) AS CNT FROM NGWL_TEST_SYSTEM.LEASE L WHERE L.SECOND_SUBJECT_CODE = 'TYLSCCSNCZ' UNION ALL SELECT 'CONTRACT_MANAGE_DETAIL', COUNT(*) FROM NGWL_TEST_SYSTEM.CONTRACT_MANAGE_DETAIL CD WHERE CD.SECOND_SUBJECT_CODE = 'TYLSCCSNCZ' UNION ALL SELECT 'BILLING_RULES', COUNT(*) FROM NGWL_TEST_BMS.BILLING_RULES BR WHERE BR.SECOND_SUBJECT_CODE = 'TYLSCCSNCZ' UNION ALL SELECT 'BILLING_STATEMENT', COUNT(*) FROM NGWL_TEST_BMS.BILLING_STATEMENT BS WHERE BS.SECOND_SUBJECT_CODE = 'TYLSCCSNCZ'; -- ============================================================================ -- 步骤 0.5 备份(必须执行;回滚的唯一依靠) -- ============================================================================ CREATE TABLE NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT_BAK_20260817 AS SELECT * FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT; -- 大表只备份科目四列 + 主键,避免整表复制 CREATE TABLE NGWL_TEST_BMS.SUBJ_BAK_20260817 AS SELECT BILLING_STATEMENT_ID, FIRST_SUBJECT_CODE, FIRST_SUBJECT_NAME, SECOND_SUBJECT_CODE, SECOND_SUBJECT_NAME FROM NGWL_TEST_BMS.BILLING_STATEMENT WHERE SECOND_SUBJECT_CODE IS NULL AND FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006'); -- ============================================================================ -- 步骤 1 迁移映射表(本次改造的"事实清单",新旧对照唯一来源) -- NEW_FIRST 编码为建议值【需业务确认,尤其与 NC 科目对照】,确认后如改动需同步改步骤2/3 -- ============================================================================ CREATE TABLE NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 ( OLD_CODE VARCHAR(100), -- 现平级科目编码(将成为二级科目编码,不变) NEW_FIRST_CODE VARCHAR(100), -- 归属一级科目编码 NEW_FIRST_NAME VARCHAR(200) ); INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2001', 'CCSR', '仓储收入'); -- 恒温仓仓租 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2002', 'CCSR', '仓储收入'); -- 普通仓室内仓租 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2003', 'CCSR', '仓储收入'); -- 临时仓储室内仓租 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2004', 'CCSR', '仓储收入'); -- 室外空地仓租 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('TYLSCCSNCZ', 'CCSR', '仓储收入'); -- 临时仓储室内仓租(重复编码,本期仅重挂不停用) INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('1001', 'PSSR', '配送收入'); -- 运费 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('1007', 'ZZSR', '增值服务收入'); -- 装卸费 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2005', 'WYSR', '物业收入'); -- 电费 INSERT INTO NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 VALUES ('2006', 'WYSR', '物业收入'); -- 水费 COMMIT; -- ============================================================================ -- 步骤 2 新增 5 个一级科目(费用类别) -- TOP_ORGANIZATION_ID 用步骤0.1盘出的 :ORG_ID(示例 2827); -- SHOW_SEQ 排在现有科目之前(一级优先展示);ACCOUNT_EXPENSE_TYPE=1 收入侧 -- ============================================================================ INSERT INTO NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT (TOP_ORGANIZATION_ID, SUBJECT_CODE, SUBJECT_NAME, SHOW_SEQ, FIRST_SUBJECT_FLAG, UPPER_SUBJECT_CODE, UPPER_SUBJECT, STATUS, ACCOUNT_EXPENSE_TYPE, DEL_FLAG) VALUES (2827, 'CCSR', '仓储收入', 1, 1, NULL, NULL, 1, 1, 1), (2827, 'PSSR', '配送收入', 2, 1, NULL, NULL, 1, 1, 1), (2827, 'ZZSR', '增值服务收入', 3, 1, NULL, NULL, 1, 1, 1), (2827, 'WYSR', '物业收入', 4, 1, NULL, NULL, 1, 1, 1), (2827, 'QT', '其他', 9, 1, NULL, NULL, 1, 1, 1); COMMIT; -- ============================================================================ -- 步骤 3 现有平级科目降为二级并挂到一级下(编码/名称不变,只改树关系) -- ============================================================================ UPDATE NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA SET (EA.FIRST_SUBJECT_FLAG, EA.UPPER_SUBJECT_CODE, EA.UPPER_SUBJECT) = (SELECT 2, M.NEW_FIRST_CODE, M.NEW_FIRST_NAME FROM NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M WHERE M.OLD_CODE = EA.SUBJECT_CODE) WHERE EXISTS (SELECT 1 FROM NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M WHERE M.OLD_CODE = EA.SUBJECT_CODE); COMMIT; -- 验证:SELECT * FROM ... WHERE FIRST_SUBJECT_FLAG=2 AND UPPER_SUBJECT_CODE IS NULL; -- 应为 0 行 -- ============================================================================ -- 步骤 4 刷历史冗余字段(6 张表,模式一致:旧一级 → 新二级,挂新一级) -- 只动 FIRST_SUBJECT_CODE 命中映射且 SECOND_SUBJECT_CODE 为空的行(平级时期的存量) -- ============================================================================ -- 4.1 计费流水(报表取数表,最重要) MERGE INTO NGWL_TEST_BMS.BILLING_STATEMENT BS USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (BS.FIRST_SUBJECT_CODE = M.OLD_CODE AND BS.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET BS.SECOND_SUBJECT_CODE = M.OLD_CODE, BS.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), BS.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, BS.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- 4.2 业务单据 MERGE INTO NGWL_TEST_BMS.BUSINESS_DOCUMENT BD USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (BD.FIRST_SUBJECT_CODE = M.OLD_CODE AND BD.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET BD.SECOND_SUBJECT_CODE = M.OLD_CODE, BD.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), BD.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, BD.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- 4.3 账单明细 MERGE INTO NGWL_TEST_BMS.BILL_DETAIL BDL USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (BDL.FIRST_SUBJECT_CODE = M.OLD_CODE AND BDL.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET BDL.SECOND_SUBJECT_CODE = M.OLD_CODE, BDL.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), BDL.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, BDL.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- 4.4 计费规则 MERGE INTO NGWL_TEST_BMS.BILLING_RULES BR USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (BR.FIRST_SUBJECT_CODE = M.OLD_CODE AND BR.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET BR.SECOND_SUBJECT_CODE = M.OLD_CODE, BR.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), BR.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, BR.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- 4.5 任务流转记录 MERGE INTO NGWL_TEST_BMS.TASK_FLOW_RECORDS TF USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (TF.FIRST_SUBJECT_CODE = M.OLD_CODE AND TF.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET TF.SECOND_SUBJECT_CODE = M.OLD_CODE, TF.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), TF.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, TF.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- 4.6 合同明细(在 system 库;影响进行中的合同计费展示,不影响金额计算) MERGE INTO NGWL_TEST_SYSTEM.CONTRACT_MANAGE_DETAIL CD USING NGWL_TEST_SYSTEM.SUBJECT_MIG_MAP_20260817 M ON (CD.FIRST_SUBJECT_CODE = M.OLD_CODE AND CD.SECOND_SUBJECT_CODE IS NULL) WHEN MATCHED THEN UPDATE SET CD.SECOND_SUBJECT_CODE = M.OLD_CODE, CD.SECOND_SUBJECT_NAME = (SELECT EA.SUBJECT_NAME FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA WHERE EA.SUBJECT_CODE = M.OLD_CODE), CD.FIRST_SUBJECT_CODE = M.NEW_FIRST_CODE, CD.FIRST_SUBJECT_NAME = M.NEW_FIRST_NAME; COMMIT; -- ============================================================================ -- 步骤 5 验证(全部只读) -- ============================================================================ -- 5.1 科目树:5 个一级 + 9 个二级,二级全部有上级 SELECT FIRST_SUBJECT_FLAG, SUBJECT_CODE, SUBJECT_NAME, UPPER_SUBJECT_CODE, STATUS FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT WHERE SUBJECT_CODE IN ('CCSR','PSSR','ZZSR','WYSR','QT','2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006') ORDER BY FIRST_SUBJECT_FLAG, UPPER_SUBJECT_CODE, SUBJECT_CODE; -- 5.2 报表口径守恒:刷数前后按月总金额必须一致(金额只挪科目不增减) SELECT SUBSTR(TO_CHAR(BUSINESS_DATE,'YYYY-MM'),1,7) AS YM, FIRST_SUBJECT_NAME, SECOND_SUBJECT_NAME, SUM(BILLING_AMOUNT) AS AMT FROM NGWL_TEST_BMS.BILLING_STATEMENT WHERE BILLING_STATE IN (2,3) AND RED_FLUSH_STATE = 1 AND DEL_FLAG = 1 AND ACCOUNT_EXPENSE_TYPE = 1 GROUP BY SUBSTR(TO_CHAR(BUSINESS_DATE,'YYYY-MM'),1,7), FIRST_SUBJECT_NAME, SECOND_SUBJECT_NAME ORDER BY 1, 2, 3; -- 与执行前同查询结果比对:合计列逐月相等;差异只应出现在科目归属列 -- 5.3 无漏网:仍以旧编码当一级、二级为空的行应为 0 SELECT COUNT(*) FROM NGWL_TEST_BMS.BILLING_STATEMENT WHERE SECOND_SUBJECT_CODE IS NULL AND FIRST_SUBJECT_CODE IN ('2001','2002','2003','2004','TYLSCCSNCZ','1001','1007','2005','2006'); -- 5.4 新推送回归:迁移后触发一次租赁出账/装卸费推送,检查 billing_statement 新行 -- FIRST_SUBJECT_NAME='仓储收入'、SECOND_SUBJECT_NAME='临时仓储室内仓租' 即为解析正确 -- ============================================================================ -- 回滚(仅在验证失败时) -- ============================================================================ -- 1) 恢复科目树: -- MERGE INTO NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT EA -- USING NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT_BAK_20260817 B -- ON (EA.EXPENSE_ACCOUNT_ID = B.EXPENSE_ACCOUNT_ID) -- WHEN MATCHED THEN UPDATE SET -- EA.FIRST_SUBJECT_FLAG = B.FIRST_SUBJECT_FLAG, -- EA.UPPER_SUBJECT_CODE = B.UPPER_SUBJECT_CODE, -- EA.UPPER_SUBJECT = B.UPPER_SUBJECT; -- 删除新增一级:DELETE FROM NGWL_TEST_SYSTEM.EXPENSE_ACCOUNT WHERE SUBJECT_CODE IN ('CCSR','PSSR','ZZSR','WYSR','QT'); -- 2) 恢复流水科目: -- MERGE INTO NGWL_TEST_BMS.BILLING_STATEMENT BS -- USING NGWL_TEST_BMS.SUBJ_BAK_20260817 B -- ON (BS.BILLING_STATEMENT_ID = B.BILLING_STATEMENT_ID) -- WHEN MATCHED THEN UPDATE SET -- BS.FIRST_SUBJECT_CODE = B.FIRST_SUBJECT_CODE, BS.FIRST_SUBJECT_NAME = B.FIRST_SUBJECT_NAME, -- BS.SECOND_SUBJECT_CODE = B.SECOND_SUBJECT_CODE, BS.SECOND_SUBJECT_NAME = B.SECOND_SUBJECT_NAME; -- COMMIT; -- ============================================================================ -- 备注 -- 1. TYLSCCSNCZ 与 2003 同名重复科目:本期仅同样重挂为"仓储收入"二级(报表按名称归组已合并成一行)。 -- 彻底去重(改 LEASE/合同引用 → 停用 TYLSCCSNCZ)作为后续主数据治理,引用量见步骤 0.3。 -- 2. 模板中"散租/固定出租"二级口径:现有科目按仓库类型命名(恒温仓/普通仓/室外空地/临时仓储), -- 仅凭编码无法区分计费模式,本期保持原名挂二级;若业务要求改成散租/固定出租口径, -- 需结合 LEASE.LEASE_TYPE(整租/散租) 对仓租流水逐一判定后改映射表重跑,属二期。 -- 3. 新一级编码 CCSR/PSSR/ZZSR/WYSR/QT 为建议值,上线前与业务/NC 对账确认,改动需同步步骤1/2。 -- ============================================================================