Files
tuanwei-java/sql/20261110_backfill_college_id.sql
T
weicw 5b7ddd48c7 feat(spec58): 4 张业务表 college_id 一次性回填脚本
以学生档案 gxmu_student.college_id 为准,按 user_id 回填 sjqn / tzbcy_form /
qmgc_form / wxxzx_project 的 college_id;仍为空的行再用表单里的学院文本
按组织名 LIKE 兜底,只有唯一命中才写入,无法匹配留空。

- A 段学生档案主路径,优先取在校记录的学院,档案学院不唯一时留空不猜
- B 段文本兜底,候选学院 = 有平台单位号的节点 ∪ 学生档案引用过的节点
- 只填空不覆盖,可重复执行;不接定时任务
- 附回填率、空值清单、未匹配文本一览与一致性抽查(3a-3d),供人工核对
2026-11-03 22:28:49 +08:00

408 lines
24 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- spec #58 / T6(issue #64):4 张在跑业务表的 college_id 一次性回填
--
-- 做什么:把「申报记录 → 申报人所在学院」写进 college_id。学院标识与
-- gxmu_student.college_id 是同一套 ID,都指向 sys_organization.organization_id
-- (见 docs/research/sso-org-data.md:24、ADR 0006「学院标识」)。
--
-- 执行顺序(不可打乱):
-- 1) 先执行 sql/20261110_spec58_approval_chain.sql(#59),它负责加 4 个 college_id 列。
-- 2) 整文件执行一次本脚本(mysql < 本文件)。文件内的段落顺序即执行顺序:
-- 0) 前置检查(只读) —— 先确认列在、名册有学院数据,否则后面的回填全是空转
-- 1) A 段 学生档案主路径 —— 申报记录 user_id → gxmu_student.college_id
-- 2) B 段 表单文本兜底 —— A 段后仍为空的行,用表单里的学院文本按组织名 LIKE 匹配
-- 3) 校验与清单(只读) —— 回填率、空值清单、未匹配文本一览,抄进上线记录
-- 3) ⚠ 只执行一次,不接定时任务。
--
-- 可重复执行:所有 UPDATE 都带「只填空、不覆盖」条件(college_id IS NULL),且不带会话状态
-- (不用临时表、不用视图),任何一句都可以单独重跑。重跑不会改掉人工修对的值。
--
-- 回滚:
-- #59 加列时 4 个 college_id 都是 NULL,本脚本只把 NULL 填成值,所以回滚就是把它们放回 NULL。
-- 执行前若已有人工填过的值,先自行快照(示例,取消注释后单独执行):
-- -- CREATE TABLE bak_20261110_college_id (module VARCHAR(64), rid BIGINT UNSIGNED, college_id INT, PRIMARY KEY (module, rid));
-- -- INSERT INTO bak_20261110_college_id SELECT 'gxmu_sjqn', id, college_id FROM gxmu_sjqn WHERE college_id IS NOT NULL;
-- (另 3 张表同形,module 换表名即可。)
-- 回滚语句见文件末尾「回滚」段,默认注释掉,避免整文件执行时误清空。
--
-- 口径(写清楚,避免二次解释):
-- * 「学院」以学生档案 gxmu_student.college_id 为准,它存的是 sys_organization.organization_id,
-- 与 gxmu_college.id 不是一套(缺口 G1,见调研文档)。本脚本不碰 gxmu_college。
-- * 学生档案优先取 status = 1(在校)那条的学院;同一 user_id 下学院不唯一时留空、不猜。
-- * 文本兜底只在「文本里恰好出现一个候选学院组织名」时写入;命中 0 个或多个都留空进清单。
-- * 候选学院节点 = 有平台单位号的节点(organization_code 非空,即开放平台同步的院系)
-- ∪ 学生档案实际引用过的节点。这样既不会把「药学院党委」这类手工节点当学院,
-- 也不用把学校根节点 ID 硬编码进脚本。
-- * 只处理 deleted = 0 的行;软删记录不参与回填,也不进回填率分母。
-- * 只按 user_id 关联学生(sys_user.user_id 全局唯一),tenant_id 不参与匹配。
-- * 不做学院层级推导、不建缺失的组织节点;匹配不上就是留空进清单。
-- ============================================================================
-- ---------------------------------------------------------------------------
-- 0) 前置检查(只读)
-- college_id_columns 期望 4;students_with_college 期望 > 0(为 0 说明名册还没同步,先别跑回填)。
-- ---------------------------------------------------------------------------
SELECT COUNT(*) AS college_id_columns
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME IN ('gxmu_sjqn', 'gxmu_tzbcy_form', 'gxmu_qmgc_form', 'gxmu_wxxzx_project')
AND COLUMN_NAME = 'college_id';
SELECT COUNT(*) AS students_with_college
FROM gxmu_student
WHERE deleted = 0 AND college_id IS NOT NULL;
-- ---------------------------------------------------------------------------
-- 1) A 段:学生档案主路径(以 gxmu_student.college_id 为准)
-- 同一个人(user_id)在名册里可能有多条:本科毕业的旧行 + 在读研究生行。
-- 取法:该 user_id 下学院唯一 → 直接用它;学院不唯一但「在校(status = 1)的学院」唯一
-- → 用在校那条的学院(同一个人的本科学院与研究生学院不同时,以在修为准)。
-- 两者都不满足 → 留空,不猜,交给 B 段文本或人工。
-- 只填空:WHERE t.college_id IS NULL;软删行与无申报人的行不处理。
-- no-op 说明:子查询无匹配行时 CASE 返回 NULL,等于把 NULL 再写一遍,不会破坏任何值。
-- ---------------------------------------------------------------------------
UPDATE gxmu_sjqn t
SET college_id = (
SELECT CASE
WHEN COUNT(DISTINCT s.college_id) = 1 THEN MIN(s.college_id)
WHEN COUNT(DISTINCT CASE WHEN s.status = 1 THEN s.college_id END) = 1
THEN MIN(CASE WHEN s.status = 1 THEN s.college_id END)
END
FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL
)
WHERE t.college_id IS NULL AND t.deleted = 0 AND t.user_id IS NOT NULL;
UPDATE gxmu_tzbcy_form t
SET college_id = (
SELECT CASE
WHEN COUNT(DISTINCT s.college_id) = 1 THEN MIN(s.college_id)
WHEN COUNT(DISTINCT CASE WHEN s.status = 1 THEN s.college_id END) = 1
THEN MIN(CASE WHEN s.status = 1 THEN s.college_id END)
END
FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL
)
WHERE t.college_id IS NULL AND t.deleted = 0 AND t.user_id IS NOT NULL;
UPDATE gxmu_qmgc_form t
SET college_id = (
SELECT CASE
WHEN COUNT(DISTINCT s.college_id) = 1 THEN MIN(s.college_id)
WHEN COUNT(DISTINCT CASE WHEN s.status = 1 THEN s.college_id END) = 1
THEN MIN(CASE WHEN s.status = 1 THEN s.college_id END)
END
FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL
)
WHERE t.college_id IS NULL AND t.deleted = 0 AND t.user_id IS NOT NULL;
UPDATE gxmu_wxxzx_project t
SET college_id = (
SELECT CASE
WHEN COUNT(DISTINCT s.college_id) = 1 THEN MIN(s.college_id)
WHEN COUNT(DISTINCT CASE WHEN s.status = 1 THEN s.college_id END) = 1
THEN MIN(CASE WHEN s.status = 1 THEN s.college_id END)
END
FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL
)
WHERE t.college_id IS NULL AND t.deleted = 0 AND t.user_id IS NOT NULL;
-- ---------------------------------------------------------------------------
-- 2) B 段:表单文本兜底(A 段后仍为空的行)
-- 每张表的学院列名不同,逐个写(见 issue #64):
-- gxmu_sjqn.unit_name / gxmu_tzbcy_form.school_name /
-- gxmu_qmgc_form.school_info / gxmu_wxxzx_project.college_grade_class
-- 匹配方式:文本里包含某个候选学院的完整组织名(LIKE '%组织名%')。
-- 只有「恰好命中 1 个候选学院」才写入;命中 0 个或多个一律留空,进 3b/3c 的清单。
-- 候选学院节点 = 有平台单位号(organization_code 非空,开放平台同步的院系)
-- ∪ 学生档案实际引用过的组织节点。
-- 这样排除「药学院党委」「护理学院团委」这类手工节点,
-- 也不用把学校根节点 ID(24)硬编码进脚本。
-- 组织名为空的节点先排除:空名会成为 LIKE '%%' 匹配一切,把每行都变成「多义」。
-- 已知误配风险(文本包含关系,故意不猜):见 DONE 的 notes。
-- ---------------------------------------------------------------------------
-- gxmu_sjqn.unit_name(所在单位)
UPDATE gxmu_sjqn t
SET college_id = (
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(o.organization_id) END
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND t.unit_name LIKE CONCAT('%', o.organization_name, '%')
)
WHERE t.college_id IS NULL AND t.deleted = 0
AND t.unit_name IS NOT NULL AND t.unit_name <> '';
-- gxmu_tzbcy_form.school_name(学校名称)
UPDATE gxmu_tzbcy_form t
SET college_id = (
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(o.organization_id) END
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND t.school_name LIKE CONCAT('%', o.organization_name, '%')
)
WHERE t.college_id IS NULL AND t.deleted = 0
AND t.school_name IS NOT NULL AND t.school_name <> '';
-- gxmu_qmgc_form.school_info(学校、院系、年级、专业)
UPDATE gxmu_qmgc_form t
SET college_id = (
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(o.organization_id) END
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND t.school_info LIKE CONCAT('%', o.organization_name, '%')
)
WHERE t.college_id IS NULL AND t.deleted = 0
AND t.school_info IS NOT NULL AND t.school_info <> '';
-- gxmu_wxxzx_project.college_grade_class(学院、年级、班别)
UPDATE gxmu_wxxzx_project t
SET college_id = (
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(o.organization_id) END
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND t.college_grade_class LIKE CONCAT('%', o.organization_name, '%')
)
WHERE t.college_id IS NULL AND t.deleted = 0
AND t.college_grade_class IS NOT NULL AND t.college_grade_class <> '';
-- ---------------------------------------------------------------------------
-- 3a) 回填率(只读):每张表一行
-- total_rows 参与统计的行数(deleted = 0)
-- filled_rows 有学院标识的行数
-- null_rows 仍为空的行数,等于「3b 空值清单」里该 module 的行数
-- fill_rate_pct 回填率;total_rows = 0 时为 NULL(空表没有回填率)
-- 期望:4 行;total_rows = filled_rows + null_rows
-- ---------------------------------------------------------------------------
SELECT 'gxmu_sjqn' AS module, COUNT(*) AS total_rows,
SUM(college_id IS NOT NULL) AS filled_rows,
SUM(college_id IS NULL) AS null_rows,
ROUND(SUM(college_id IS NOT NULL) / NULLIF(COUNT(*), 0) * 100, 1) AS fill_rate_pct
FROM gxmu_sjqn WHERE deleted = 0
UNION ALL
SELECT 'gxmu_tzbcy_form', COUNT(*), SUM(college_id IS NOT NULL), SUM(college_id IS NULL),
ROUND(SUM(college_id IS NOT NULL) / NULLIF(COUNT(*), 0) * 100, 1)
FROM gxmu_tzbcy_form WHERE deleted = 0
UNION ALL
SELECT 'gxmu_qmgc_form', COUNT(*), SUM(college_id IS NOT NULL), SUM(college_id IS NULL),
ROUND(SUM(college_id IS NOT NULL) / NULLIF(COUNT(*), 0) * 100, 1)
FROM gxmu_qmgc_form WHERE deleted = 0
UNION ALL
SELECT 'gxmu_wxxzx_project', COUNT(*), SUM(college_id IS NOT NULL), SUM(college_id IS NULL),
ROUND(SUM(college_id IS NOT NULL) / NULLIF(COUNT(*), 0) * 100, 1)
FROM gxmu_wxxzx_project WHERE deleted = 0;
-- ---------------------------------------------------------------------------
-- 3b) 空值清单(只读):本次没能回填上学院的行,逐行列出供人工核对
-- 输出列:
-- module 哪张表
-- id 申报记录主键(回填时直接 UPDATE ... WHERE id = ?)
-- user_id 申报人账号;为空说明这条记录没绑定登录人,两条路径都走不通
-- college_text 表单里填的学院文本(sjqn.unit_name / tzbcy_form.school_name /
-- qmgc_form.school_info / wxxzx_project.college_grade_class)
-- student_college 学生档案解析出的学院名与 ID,多个用 / 分隔;
-- 为空 = 名册里没这个人或学院没解析出来,多个 = 档案里学院不唯一
-- reason 粗分原因,便于分组处理(明细仍看 student_college 与 3c)
-- 期望:4 张表的空值行都出现在这里;填完之后可按 reason 分组人工处理。
-- ⚠ 人工处理后重跑本文件即可(只填空不覆盖),不需要删数据。
-- ---------------------------------------------------------------------------
SELECT 'gxmu_sjqn' AS module, t.id, t.user_id, t.unit_name AS college_text,
(SELECT GROUP_CONCAT(DISTINCT CONCAT(o.organization_name, '(', o.organization_id, ')') SEPARATOR ' / ')
FROM gxmu_student s JOIN sys_organization o ON o.organization_id = s.college_id
WHERE s.deleted = 0 AND s.user_id = t.user_id) AS student_college,
CASE
WHEN t.user_id IS NULL THEN '无申报人'
WHEN NOT EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL)
THEN '学生档案无学院'
WHEN (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) > 1
THEN '学生档案学院不唯一'
WHEN t.unit_name IS NULL OR t.unit_name = '' THEN '表单未填学院文本'
ELSE '文本未唯一匹配(见 3c)'
END AS reason
FROM gxmu_sjqn t
WHERE t.deleted = 0 AND t.college_id IS NULL
UNION ALL
SELECT 'gxmu_tzbcy_form', t.id, t.user_id, t.school_name,
(SELECT GROUP_CONCAT(DISTINCT CONCAT(o.organization_name, '(', o.organization_id, ')') SEPARATOR ' / ')
FROM gxmu_student s JOIN sys_organization o ON o.organization_id = s.college_id
WHERE s.deleted = 0 AND s.user_id = t.user_id),
CASE
WHEN t.user_id IS NULL THEN '无申报人'
WHEN NOT EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL)
THEN '学生档案无学院'
WHEN (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) > 1
THEN '学生档案学院不唯一'
WHEN t.school_name IS NULL OR t.school_name = '' THEN '表单未填学院文本'
ELSE '文本未唯一匹配(见 3c)'
END
FROM gxmu_tzbcy_form t
WHERE t.deleted = 0 AND t.college_id IS NULL
UNION ALL
SELECT 'gxmu_qmgc_form', t.id, t.user_id, t.school_info,
(SELECT GROUP_CONCAT(DISTINCT CONCAT(o.organization_name, '(', o.organization_id, ')') SEPARATOR ' / ')
FROM gxmu_student s JOIN sys_organization o ON o.organization_id = s.college_id
WHERE s.deleted = 0 AND s.user_id = t.user_id),
CASE
WHEN t.user_id IS NULL THEN '无申报人'
WHEN NOT EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL)
THEN '学生档案无学院'
WHEN (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) > 1
THEN '学生档案学院不唯一'
WHEN t.school_info IS NULL OR t.school_info = '' THEN '表单未填学院文本'
ELSE '文本未唯一匹配(见 3c)'
END
FROM gxmu_qmgc_form t
WHERE t.deleted = 0 AND t.college_id IS NULL
UNION ALL
SELECT 'gxmu_wxxzx_project', t.id, t.user_id, t.college_grade_class,
(SELECT GROUP_CONCAT(DISTINCT CONCAT(o.organization_name, '(', o.organization_id, ')') SEPARATOR ' / ')
FROM gxmu_student s JOIN sys_organization o ON o.organization_id = s.college_id
WHERE s.deleted = 0 AND s.user_id = t.user_id),
CASE
WHEN t.user_id IS NULL THEN '无申报人'
WHEN NOT EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL)
THEN '学生档案无学院'
WHEN (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) > 1
THEN '学生档案学院不唯一'
WHEN t.college_grade_class IS NULL OR t.college_grade_class = '' THEN '表单未填学院文本'
ELSE '文本未唯一匹配(见 3c)'
END
FROM gxmu_wxxzx_project t
WHERE t.deleted = 0 AND t.college_id IS NULL;
-- ---------------------------------------------------------------------------
-- 3c) 未匹配文本一览(只读):把空值清单里的学院文本去重,看它到底命中了哪些组织名
-- matched_names 为空 = 文本里没有任何候选学院名(填错了 / 写的是简称 / 不是学院);
-- 出现 2 个以上 = 组织树里有同义节点(如「生命科学与医学工程学院」与「生命科学与医学工程」),
-- 这种按「宁可留空」的口径留给人工判断,不自动挑一个。
-- row_count 是该文本下仍未回填的行数。
-- ---------------------------------------------------------------------------
SELECT 'gxmu_sjqn' AS module, v.college_text, v.row_count,
(SELECT GROUP_CONCAT(o.organization_name ORDER BY o.organization_name SEPARATOR ' / ')
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND v.college_text LIKE CONCAT('%', o.organization_name, '%')) AS matched_names
FROM (SELECT unit_name AS college_text, COUNT(*) AS row_count
FROM gxmu_sjqn WHERE deleted = 0 AND college_id IS NULL
GROUP BY unit_name) v
UNION ALL
SELECT 'gxmu_tzbcy_form', v.college_text, v.row_count,
(SELECT GROUP_CONCAT(o.organization_name ORDER BY o.organization_name SEPARATOR ' / ')
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND v.college_text LIKE CONCAT('%', o.organization_name, '%'))
FROM (SELECT school_name AS college_text, COUNT(*) AS row_count
FROM gxmu_tzbcy_form WHERE deleted = 0 AND college_id IS NULL
GROUP BY school_name) v
UNION ALL
SELECT 'gxmu_qmgc_form', v.college_text, v.row_count,
(SELECT GROUP_CONCAT(o.organization_name ORDER BY o.organization_name SEPARATOR ' / ')
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND v.college_text LIKE CONCAT('%', o.organization_name, '%'))
FROM (SELECT school_info AS college_text, COUNT(*) AS row_count
FROM gxmu_qmgc_form WHERE deleted = 0 AND college_id IS NULL
GROUP BY school_info) v
UNION ALL
SELECT 'gxmu_wxxzx_project', v.college_text, v.row_count,
(SELECT GROUP_CONCAT(o.organization_name ORDER BY o.organization_name SEPARATOR ' / ')
FROM sys_organization o
WHERE o.deleted = 0
AND o.organization_name <> ''
AND ( (o.organization_code IS NOT NULL AND o.organization_code <> '')
OR EXISTS (SELECT 1 FROM gxmu_student s WHERE s.deleted = 0 AND s.college_id = o.organization_id) )
AND v.college_text LIKE CONCAT('%', o.organization_name, '%'))
FROM (SELECT college_grade_class AS college_text, COUNT(*) AS row_count
FROM gxmu_wxxzx_project WHERE deleted = 0 AND college_id IS NULL
GROUP BY college_grade_class) v;
-- ---------------------------------------------------------------------------
-- 3d) 一致性抽查(只读)
-- a/b 段期望返回空集:回填出的学院 ID 在组织表里不存在(历史脏数据 / 节点被删)或节点已软删。
-- c 段期望每张表都是 0:有 user_id 且学生档案学院唯一、却仍为空的行;
-- 非 0 说明 A 段没执行或执行顺序被打乱。
-- ---------------------------------------------------------------------------
SELECT 'gxmu_sjqn' AS module, t.id, t.college_id
FROM gxmu_sjqn t
LEFT JOIN sys_organization o ON o.organization_id = t.college_id
WHERE t.college_id IS NOT NULL AND (o.organization_id IS NULL OR o.deleted = 1)
UNION ALL
SELECT 'gxmu_tzbcy_form', t.id, t.college_id
FROM gxmu_tzbcy_form t
LEFT JOIN sys_organization o ON o.organization_id = t.college_id
WHERE t.college_id IS NOT NULL AND (o.organization_id IS NULL OR o.deleted = 1)
UNION ALL
SELECT 'gxmu_qmgc_form', t.id, t.college_id
FROM gxmu_qmgc_form t
LEFT JOIN sys_organization o ON o.organization_id = t.college_id
WHERE t.college_id IS NOT NULL AND (o.organization_id IS NULL OR o.deleted = 1)
UNION ALL
SELECT 'gxmu_wxxzx_project', t.id, t.college_id
FROM gxmu_wxxzx_project t
LEFT JOIN sys_organization o ON o.organization_id = t.college_id
WHERE t.college_id IS NOT NULL AND (o.organization_id IS NULL OR o.deleted = 1);
-- c) 应为 0 行
SELECT 'gxmu_sjqn' AS module, COUNT(*) AS unexpected_null
FROM gxmu_sjqn t
WHERE t.deleted = 0 AND t.college_id IS NULL AND t.user_id IS NOT NULL
AND (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) = 1
UNION ALL
SELECT 'gxmu_tzbcy_form', COUNT(*)
FROM gxmu_tzbcy_form t
WHERE t.deleted = 0 AND t.college_id IS NULL AND t.user_id IS NOT NULL
AND (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) = 1
UNION ALL
SELECT 'gxmu_qmgc_form', COUNT(*)
FROM gxmu_qmgc_form t
WHERE t.deleted = 0 AND t.college_id IS NULL AND t.user_id IS NOT NULL
AND (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) = 1
UNION ALL
SELECT 'gxmu_wxxzx_project', COUNT(*)
FROM gxmu_wxxzx_project t
WHERE t.deleted = 0 AND t.college_id IS NULL AND t.user_id IS NOT NULL
AND (SELECT COUNT(DISTINCT s.college_id) FROM gxmu_student s
WHERE s.deleted = 0 AND s.user_id = t.user_id AND s.college_id IS NOT NULL) = 1;
-- ---------------------------------------------------------------------------
-- 回滚(需要时才执行;默认注释,避免整文件执行时把值清空)
-- 只清本脚本可能写过的行:把这 4 张表的 college_id 全部置回 NULL。
-- 人工修过的值也会一起清掉,所以先按文件头「快照」段留备份。
-- ---------------------------------------------------------------------------
-- UPDATE gxmu_sjqn SET college_id = NULL WHERE college_id IS NOT NULL;
-- UPDATE gxmu_tzbcy_form SET college_id = NULL WHERE college_id IS NOT NULL;
-- UPDATE gxmu_qmgc_form SET college_id = NULL WHERE college_id IS NOT NULL;
-- UPDATE gxmu_wxxzx_project SET college_id = NULL WHERE college_id IS NOT NULL;