| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- -- ===========================================================================
- -- S0-LINESKILL-TENANT-UK-CLOSURE-1 —— 产线岗位(line-post)
- --
- -- 本脚本做两件互相独立的事:
- --
- -- 【1】MULTI-TENANT SCHEMA CORRECTNESS(主要目的)
- -- LineSkillMaster / LineSkillDetail 的 UNIQUE 索引缺少 tenant_id:
- -- IX_LineSkillMaster : Domain + Site + ProdLine + JOBNo
- -- IX_LineSkillDetail : Domain + Site + ProdLine + JOBNo + SkillNo
- -- 两者都是「跨租户唯一」,一旦 Site 被启用(非 NULL),两个租户持有相同
- -- (Domain, Site, ProdLine, JOBNo) 就会互相顶撞。这属于多租户索引缺陷,
- -- 必须补 tenant_id。
- --
- -- ⚠️ 这不是为了「让下面 22 行插得进去」。已实测证明:当前 Site 全为 NULL,
- -- MySQL 的 UNIQUE 索引不对 NULL 做相等判定,因此即使不改索引,
- -- 11 master + 11 detail 也能插入成功(事务内实测 11/11 + 11/11,随后回滚)。
- -- 索引修复的唯一目的是 schema 正确性。
- --
- -- 【2】line-post 演示数据(22 行)
- -- 来源 AIDOP 797:仅取 ProdLine 命中目标租户 LineMaster 的 11 条产线;
- -- 其余 29 条因产线不存在于目标租户而丢弃,不为凑数复制。
- --
- -- Site 处理:保持 NULL。
- -- 族级证据显示 Site = 工作组/工作中心类历史列(EmpSkills.Site 注释「工作组」,
- -- AIDOPB 实测取值为 数控组/冲压组 等;ProdLineDetail.Site 注释「工作中心编码」),
- -- 且 LineSkill 全库 296 行 Site 100% 为 NULL、无 UI、无校验。
- -- 因此新行沿用 NULL —— 不得写入租户/工厂/组织编码。
- --
- -- ⚠️ 事务性说明:MySQL 的 DDL 会隐式提交,且 AutoVersionUpdate 的
- -- SqlScriptSplitter.Execute 不开启显式事务。因此本脚本**不是原子的**。
- -- 为此所有语句均写成可安全重入:
- -- - DDL 以「索引是否已包含 tenant_id」为条件,已修则整段跳过;
- -- - 数据以业务键 NOT EXISTS 兜底,重跑影响行数为 0。
- -- ===========================================================================
- SET @src := (SELECT t.Id FROM SysTenant t JOIN SysOrg o ON o.Id = t.OrgId WHERE o.Code = 'AIDOP' AND t.Status = 1 LIMIT 1);
- SET @dst := (SELECT t.Id FROM SysTenant t JOIN SysOrg o ON o.Id = t.OrgId WHERE o.Code = 'UATTEST_CHL' AND t.Status = 1 LIMIT 1);
- -- ---------------------------------------------------------------------------
- -- 01 · 前置守卫:新 UK 定义下不得存在重复(否则不能建唯一索引)
- -- 重复则抛错中止,不带病改索引。
- -- ---------------------------------------------------------------------------
- SET @dup_m := (SELECT COUNT(*) FROM (
- SELECT tenant_id, Domain, Site, ProdLine, JOBNo
- FROM LineSkillMaster GROUP BY tenant_id, Domain, Site, ProdLine, JOBNo HAVING COUNT(*) > 1) x);
- SET @dup_d := (SELECT COUNT(*) FROM (
- SELECT tenant_id, Domain, Site, ProdLine, JOBNo, SkillNo
- FROM LineSkillDetail GROUP BY tenant_id, Domain, Site, ProdLine, JOBNo, SkillNo HAVING COUNT(*) > 1) x);
- SET @g := IF(@dup_m = 0 AND @dup_d = 0, 'DO 0',
- 'SELECT RAISE_LINESKILL_UK_DUPLICATE_FOUND');
- PREPARE gstmt FROM @g;
- EXECUTE gstmt;
- DEALLOCATE PREPARE gstmt;
- -- ---------------------------------------------------------------------------
- -- 02 · IX_LineSkillMaster → 补 tenant_id
- -- 条件:该索引存在且**不含** tenant_id 才重建;已含则整段 no-op(可重入)。
- -- ---------------------------------------------------------------------------
- SET @m_fixed := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillMaster'
- AND INDEX_NAME = 'IX_LineSkillMaster' AND COLUMN_NAME = 'tenant_id');
- SET @m_exists := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillMaster'
- AND INDEX_NAME = 'IX_LineSkillMaster');
- SET @s := IF(@m_fixed = 0 AND @m_exists > 0,
- 'DROP INDEX IX_LineSkillMaster ON LineSkillMaster', 'DO 0');
- PREPARE stmt FROM @s;
- EXECUTE stmt;
- DEALLOCATE PREPARE stmt;
- SET @m_now := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillMaster'
- AND INDEX_NAME = 'IX_LineSkillMaster');
- SET @s := IF(@m_now = 0,
- 'CREATE UNIQUE INDEX IX_LineSkillMaster ON LineSkillMaster (tenant_id, Domain, Site, ProdLine, JOBNo)',
- 'DO 0');
- PREPARE stmt FROM @s;
- EXECUTE stmt;
- DEALLOCATE PREPARE stmt;
- -- ---------------------------------------------------------------------------
- -- 03 · IX_LineSkillDetail → 补 tenant_id(同样条件化、可重入)
- -- ---------------------------------------------------------------------------
- SET @d_fixed := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillDetail'
- AND INDEX_NAME = 'IX_LineSkillDetail' AND COLUMN_NAME = 'tenant_id');
- SET @d_exists := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillDetail'
- AND INDEX_NAME = 'IX_LineSkillDetail');
- SET @s := IF(@d_fixed = 0 AND @d_exists > 0,
- 'DROP INDEX IX_LineSkillDetail ON LineSkillDetail', 'DO 0');
- PREPARE stmt FROM @s;
- EXECUTE stmt;
- DEALLOCATE PREPARE stmt;
- SET @d_now := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'LineSkillDetail'
- AND INDEX_NAME = 'IX_LineSkillDetail');
- SET @s := IF(@d_now = 0,
- 'CREATE UNIQUE INDEX IX_LineSkillDetail ON LineSkillDetail (tenant_id, Domain, Site, ProdLine, JOBNo, SkillNo)',
- 'DO 0');
- PREPARE stmt FROM @s;
- EXECUTE stmt;
- DEALLOCATE PREPARE stmt;
- -- ---------------------------------------------------------------------------
- -- 04 · LineSkillMaster COPY 11
- -- 只取 ProdLine 命中目标租户 LineMaster 的产线(JOIN 即门禁);
- -- Site 保持 NULL;RecID 为 auto_increment,不赋值、不复制源主键。
- -- ---------------------------------------------------------------------------
- INSERT INTO LineSkillMaster
- (tenant_id, Domain, Site, ProdLine, JOBNo, JOBDescr, Orderby, WType, RunCrew, CreateTime, UpdateTime)
- SELECT @dst, s.Domain, NULL, s.ProdLine, s.JOBNo, s.JOBDescr, s.Orderby, s.WType, s.RunCrew, NOW(), NOW()
- FROM LineSkillMaster s
- JOIN LineMaster l ON l.tenant_id = @dst AND l.Line = s.ProdLine
- WHERE s.tenant_id = @src
- AND @src IS NOT NULL AND @dst IS NOT NULL
- AND NOT EXISTS (
- SELECT 1 FROM LineSkillMaster x
- WHERE x.tenant_id = @dst AND x.Domain = s.Domain
- AND x.ProdLine = s.ProdLine AND x.JOBNo = s.JOBNo AND x.Site IS NULL);
- -- ---------------------------------------------------------------------------
- -- 05 · LineSkillDetail COPY 11
- -- ★ 内部 ID remap(禁止复制源主键 / 源 PersonSkillId):
- -- LineSkillRecID ← 目标租户刚插入的 master RecID(按业务键回查)
- -- PersonSkillId ← 目标租户 PersonSkill.Code = 源 SkillNo 的 Id
- -- (源库该列指向的是他租户的 PersonSkill,属源侧既有缺陷,不得沿用)
- -- ---------------------------------------------------------------------------
- INSERT INTO LineSkillDetail
- (tenant_id, Domain, Site, ProdLine, JOBNo, SkillNo, LineSkillRecID, PersonSkillId,
- required_level, is_enabled, CreateTime, UpdateTime)
- SELECT @dst, m.Domain, NULL, m.ProdLine, m.JOBNo, sd.SkillNo, m.RecID, p.Id,
- sd.required_level, 1, NOW(), NOW()
- FROM LineSkillMaster m
- JOIN LineSkillMaster sm
- ON sm.tenant_id = @src AND sm.Domain = m.Domain
- AND sm.ProdLine = m.ProdLine AND sm.JOBNo = m.JOBNo AND sm.Site IS NULL
- JOIN LineSkillDetail sd
- ON sd.tenant_id = @src AND sd.LineSkillRecID = sm.RecID
- JOIN PersonSkill p
- ON p.tenant_id = @dst AND p.Code = sd.SkillNo
- WHERE m.tenant_id = @dst AND m.Site IS NULL
- AND @src IS NOT NULL AND @dst IS NOT NULL
- AND NOT EXISTS (
- SELECT 1 FROM LineSkillDetail d
- WHERE d.tenant_id = @dst AND d.LineSkillRecID = m.RecID AND d.PersonSkillId = p.Id);
|