1.0.438.sql 3.1 KB

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