|
|
@@ -0,0 +1,160 @@
|
|
|
+-- ===========================================================================
|
|
|
+-- 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);
|