| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- -- 1.0.296.sql S0 Warehouse P1:ItemPackMaster / NbrControl 唯一索引租户化(幂等 + 冲突前置守卫)。
- -- 目的:唯一约束从「全局业务键」改为「租户内业务键」,使不同租户可复用相同业务编码,
- -- 同一租户内重复业务键仍被数据库拒绝。
- -- 不改字段类型、不改 tenant_id 可空性、不插入/更新/删除任何业务数据、不动其它表索引。
- -- 业务键依据(information_schema 实证 + 消费链路审计):
- -- ItemPackMaster:历史唯一键 (Domain, ItemNum);Domain 与 factory_ref_id 在数据中 1:1;
- -- tenant 内唯一键 = (tenant_id, Domain, ItemNum)。
- -- NbrControl :历史唯一键 (domain_code, NbrType);domain_code NOT NULL(现值全为空串,作安全唯一锚),
- -- NbrType 为真实单号类型码;tenant 内唯一键 = (tenant_id, domain_code, NbrType)。
- -- ============================================================================
- -- ========== 一、冲突前置检测(同租户内重复则跳过创建,绝不自动清理历史数据) ==========
- SET @ipk_dup := (SELECT COUNT(*) FROM (
- SELECT tenant_id, `Domain`, ItemNum
- FROM ItemPackMaster
- GROUP BY tenant_id, `Domain`, ItemNum
- HAVING COUNT(*) > 1) t);
- SET @nbr_dup := (SELECT COUNT(*) FROM (
- SELECT tenant_id, domain_code, NbrType
- FROM NbrControl
- GROUP BY tenant_id, domain_code, NbrType
- HAVING COUNT(*) > 1) t);
- SELECT @ipk_dup AS itempack_intratenant_dup, @nbr_dup AS nbrcontrol_intratenant_dup;
- -- 期望均为 0;若非 0,本脚本不会创建新唯一索引(下方 ADD 被守卫跳过),需人工治理后重跑。
- -- ========== 二、ItemPackMaster:旧全局唯一索引 -> 租户内唯一索引 ==========
- -- 2.1 删除旧唯一索引 uk_ItemPackMaster_domain_itemnum(存在才删)
- SET @has_old_ipk := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ItemPackMaster'
- AND INDEX_NAME = 'uk_ItemPackMaster_domain_itemnum');
- SET @sql := IF(@has_old_ipk > 0,
- 'ALTER TABLE ItemPackMaster DROP INDEX uk_ItemPackMaster_domain_itemnum', 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- 2.2 创建新租户内唯一索引(不存在且无同租户冲突才建)
- SET @has_new_ipk := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ItemPackMaster'
- AND INDEX_NAME = 'uk_ItemPackMaster_tenant_domain_itemnum');
- SET @sql := IF(@has_new_ipk = 0 AND @ipk_dup = 0,
- 'ALTER TABLE ItemPackMaster ADD UNIQUE INDEX uk_ItemPackMaster_tenant_domain_itemnum (tenant_id, `Domain`, ItemNum)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- 2.3 处理隐藏的第二个全局唯一索引 ix_ItemPackMaster (Domain, ItemNum)。
- -- 该索引 NON_UNIQUE=0(唯一),与被删的 uk 同样强制全局唯一,会继续阻断跨租户相同 (Domain, ItemNum),必须一并处理。
- -- 方案:删除其“唯一”版本,重建为“非唯一”同名索引(保留 (Domain, ItemNum) 查询性能,但不再强制全局唯一)。
- SET @ix_unique := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ItemPackMaster'
- AND INDEX_NAME = 'ix_ItemPackMaster' AND NON_UNIQUE = 0);
- SET @sql := IF(@ix_unique > 0,
- 'ALTER TABLE ItemPackMaster DROP INDEX ix_ItemPackMaster', 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- SET @has_ix := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ItemPackMaster'
- AND INDEX_NAME = 'ix_ItemPackMaster');
- SET @sql := IF(@has_ix = 0,
- 'ALTER TABLE ItemPackMaster ADD INDEX ix_ItemPackMaster (`Domain`, ItemNum)', 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ========== 三、NbrControl:旧全局唯一索引 -> 租户内唯一索引 ==========
- -- 3.1 删除旧唯一索引 uk_NbrControl_domain_nbrtype(存在才删)
- SET @has_old_nbr := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'NbrControl'
- AND INDEX_NAME = 'uk_NbrControl_domain_nbrtype');
- SET @sql := IF(@has_old_nbr > 0,
- 'ALTER TABLE NbrControl DROP INDEX uk_NbrControl_domain_nbrtype', 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- 3.2 创建新租户内唯一索引(不存在且无同租户冲突才建)
- SET @has_new_nbr := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'NbrControl'
- AND INDEX_NAME = 'uk_NbrControl_tenant_domain_nbrtype');
- SET @sql := IF(@has_new_nbr = 0 AND @nbr_dup = 0,
- 'ALTER TABLE NbrControl ADD UNIQUE INDEX uk_NbrControl_tenant_domain_nbrtype (tenant_id, domain_code, NbrType)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ========== 四、执行后验证 ==========
- SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE, SEQ_IN_INDEX, COLUMN_NAME
- FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE()
- AND TABLE_NAME IN ('ItemPackMaster', 'NbrControl')
- AND INDEX_NAME <> 'PRIMARY'
- ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX;
- -- 期望:ItemPackMaster 仅 uk_ItemPackMaster_tenant_domain_itemnum(唯一) + ix_ItemPackMaster(NON_UNIQUE=1);
- -- NbrControl 仅 uk_NbrControl_tenant_domain_nbrtype(唯一)。均无全局唯一 (Domain,ItemNum)/(domain_code,NbrType)。
- -- ============================================================================
- -- 回滚 SQL(如需恢复到全局唯一索引,历史数据无跨租户同键冲突时可执行):
- -- ALTER TABLE ItemPackMaster DROP INDEX uk_ItemPackMaster_tenant_domain_itemnum;
- -- ALTER TABLE ItemPackMaster DROP INDEX ix_ItemPackMaster;
- -- ALTER TABLE ItemPackMaster ADD UNIQUE INDEX uk_ItemPackMaster_domain_itemnum (`Domain`, ItemNum);
- -- ALTER TABLE ItemPackMaster ADD UNIQUE INDEX ix_ItemPackMaster (`Domain`, ItemNum);
- -- ALTER TABLE NbrControl DROP INDEX uk_NbrControl_tenant_domain_nbrtype;
- -- ALTER TABLE NbrControl ADD UNIQUE INDEX uk_NbrControl_domain_nbrtype (domain_code, NbrType);
- -- 注意:回滚前必须确认不存在跨租户相同业务键,否则回滚 ADD 会因重复而失败(这正是本次改造要放开的场景)。
- -- ============================================================================
|