-- 修复重复朋友圈配置,并保证同一用户最多只有一条有效配置。 -- 适用 MySQL 5.7 / 8.0。执行前选择业务库,并备份 member_recent_cfg。 -- 先部署兼容重复数据/唯一键冲突的代码;执行期间暂停配置写入(登录初始化、隐私设置、后台编辑)。 -- DDL 会隐式提交;如中途失败,修复原因后可重跑,不能依靠 ROLLBACK 撤销整个脚本。 -- 只逻辑删除较旧行,不重建默认配置,不修改保留行的隐私/通知选项。 -- member_id 引用 member_user 的全局主键,唯一键不额外拼接可能为 NULL 的 tenant_id。 -- 预检:完整展示重复配置,便于核对 home_hide/global_hide/privacy_secure/short_note/is_push 等实际字段。 -- 保留顺序与服务查询一致:update_time 最新优先,时间相同取 id 最大;NULL 更新时间排在最后。 SELECT cfg.* FROM `member_recent_cfg` cfg INNER JOIN ( SELECT `member_id`, COUNT(*) AS active_count FROM `member_recent_cfg` WHERE `deleted` = b'0' GROUP BY `member_id` HAVING COUNT(*) > 1 ) duplicated ON duplicated.`member_id` = cfg.`member_id` WHERE cfg.`deleted` = b'0' ORDER BY cfg.`member_id`, cfg.`update_time` DESC, cfg.`id` DESC; -- 先物化待删除 id,再更新,避免修改 update_time 影响同一批次的保留顺序。 -- GROUP BY 阻止 MySQL 5.7/8 合并派生表,避免 1093 自查询更新错误。 UPDATE `member_recent_cfg` obsolete INNER JOIN ( SELECT older.`id` FROM `member_recent_cfg` older INNER JOIN `member_recent_cfg` newer ON newer.`member_id` = older.`member_id` AND newer.`deleted` = b'0' AND ( newer.`update_time` > older.`update_time` OR (older.`update_time` IS NULL AND newer.`update_time` IS NOT NULL) OR (newer.`update_time` <=> older.`update_time` AND newer.`id` > older.`id`) ) WHERE older.`deleted` = b'0' GROUP BY older.`id` ) duplicate_ids ON duplicate_ids.`id` = obsolete.`id` SET obsolete.`deleted` = b'1', obsolete.`update_time` = CURRENT_TIMESTAMP WHERE obsolete.`deleted` = b'0'; SELECT ROW_COUNT() AS logically_deleted_duplicate_configs; -- 已删除行生成 NULL,保留所有删除历史,同时允许以后重新创建配置。 SET @cfg_add_column_sql = IF( (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'member_recent_cfg' AND COLUMN_NAME = 'active_unique_flag') = 0, 'ALTER TABLE `member_recent_cfg` ADD COLUMN `active_unique_flag` tinyint GENERATED ALWAYS AS (CASE WHEN `deleted` = 0 THEN 1 ELSE NULL END) STORED COMMENT ''有效配置唯一标记,删除记录为NULL''', 'SELECT ''active_unique_flag already exists'' AS migration_step' ); PREPARE cfg_stmt FROM @cfg_add_column_sql; EXECUTE cfg_stmt; DEALLOCATE PREPARE cfg_stmt; SET @cfg_add_index_sql = IF( (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'member_recent_cfg' AND INDEX_NAME = 'uk_member_recent_cfg_active') = 0, 'ALTER TABLE `member_recent_cfg` ADD UNIQUE KEY `uk_member_recent_cfg_active` (`member_id`, `active_unique_flag`)', 'SELECT ''uk_member_recent_cfg_active already exists'' AS migration_step' ); PREPARE cfg_stmt FROM @cfg_add_index_sql; EXECUTE cfg_stmt; DEALLOCATE PREPARE cfg_stmt; -- 验收:应无重复分组;索引 NON_UNIQUE 应为 0。 SELECT `member_id`, COUNT(*) AS active_count FROM `member_recent_cfg` WHERE `deleted` = b'0' GROUP BY `member_id` HAVING COUNT(*) > 1; SHOW INDEX FROM `member_recent_cfg` WHERE Key_name = 'uk_member_recent_cfg_active';