-- =========================================================================== -- S6-FUNC-004-TOOLING-LEDGER-CRUD-1 -- MoldToolList(模工具台账)补租户内业务编码唯一索引 -- -- 目的:MoldTypeCode 是模工具台账的业务编码,且被下游按值引用 -- (ProdLineDetail.MoldTypeCode 同租户命中 896 行)。 -- 本批为 FUNC-S6-004 增加 CRUD 后,必须由 DB 约束保证租户内唯一 —— -- 仅靠应用层「先查后插」存在 TOCTOU 竞态,并发下会插入重复编码。 -- -- 范围:**只新增一个唯一索引**。 -- 不修改 tenant_id 可空性、不改任何列定义、不改字符集/collation、 -- 不清洗历史数据、不删除任何行。 -- -- 前置实测(2026-09-01,aidopdev,只读): -- MoldToolList 共 290 行,全部 tenant_id = 824585161322565 -- tenant_id IS NULL = 0 -- MoldTypeCode IS NULL = 0 -- TRIM(MoldTypeCode) = '' = 0 -- MoldTypeCode <> TRIM(...) = 0 -- GROUP BY tenant_id,MoldTypeCode HAVING COUNT(*)>1 = 0 组 -- 现有索引:仅 PRIMARY(id) -- -- ⚠️ tenant_id 可空的已知边界:MySQL 的 UNIQUE 不对 NULL 做相等判定, -- 因此若将来出现 tenant_id IS NULL 的行,唯一性对该行不生效。 -- 当前 0 行,且应用层写入一律由服务端从 JWT 盖章 tenant_id,不会产生 NULL。 -- 本批**不**把 tenant_id 改成 NOT NULL(属独立的 schema 变更,且实体侧 -- ITenantIdFilter.TenantId 为 long?,硬化会被 CodeFirst 还原)。 -- -- ⚠️ 事务性:MySQL DDL 隐式提交,AutoVersionUpdate 不开显式事务。 -- 故所有语句写成可安全重入:索引已存在则整段 no-op。 -- =========================================================================== -- --------------------------------------------------------------------------- -- 01 · 前置守卫:新唯一键定义下不得存在重复,否则中止,不带病建索引、不清洗数据 -- --------------------------------------------------------------------------- SET @dup := (SELECT COUNT(*) FROM ( SELECT tenant_id, MoldTypeCode FROM MoldToolList GROUP BY tenant_id, MoldTypeCode HAVING COUNT(*) > 1) x); SET @g := IF(@dup = 0, 'DO 0', 'SELECT RAISE_MOLDTOOLLIST_DUPLICATE_TYPECODE_FOUND'); PREPARE gstmt FROM @g; EXECUTE gstmt; DEALLOCATE PREPARE gstmt; -- --------------------------------------------------------------------------- -- 02 · 建唯一索引 uk_MoldToolList_tenant_type_code(tenant_id, MoldTypeCode) -- 条件:同名索引不存在才建;已存在则整段 no-op(可重入) -- --------------------------------------------------------------------------- SET @idx_exists := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'MoldToolList' AND INDEX_NAME = 'uk_MoldToolList_tenant_type_code'); SET @s := IF(@idx_exists = 0, 'CREATE UNIQUE INDEX uk_MoldToolList_tenant_type_code ON MoldToolList (tenant_id, MoldTypeCode)', 'DO 0'); PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;