-- ============================================================================= -- drop_all_unique_indexes.sql -- 去掉当前库所有非 PRIMARY 唯一索引的「唯一性」(改为同名普通 INDEX) -- 流程:备份并删除外键 → UNIQUE 改 KEY → 恢复外键 -- 解决 Error 1553:Cannot drop index ... needed in a foreign key constraint -- 可重复执行;唯一性请在业务代码中校验。 -- 请在 Navicat / DBeaver / MySQL Workbench 中整段执行(需要 DELIMITER)。 -- ============================================================================= SET @db := DATABASE(); -- ---------- 预览:将处理的唯一索引 ---------- SELECT TABLE_NAME, INDEX_NAME, GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS columns FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND NON_UNIQUE = 0 AND INDEX_NAME <> 'PRIMARY' GROUP BY TABLE_NAME, INDEX_NAME ORDER BY TABLE_NAME, INDEX_NAME; -- ---------- 预览:库中物理外键(会临时删除再重建)---------- SELECT rc.CONSTRAINT_NAME, rc.TABLE_NAME, rc.REFERENCED_TABLE_NAME, rc.UPDATE_RULE, rc.DELETE_RULE FROM information_schema.REFERENTIAL_CONSTRAINTS rc WHERE rc.CONSTRAINT_SCHEMA = @db ORDER BY rc.TABLE_NAME, rc.CONSTRAINT_NAME; DROP PROCEDURE IF EXISTS `sp_drop_all_unique_indexes`; DELIMITER $$ CREATE PROCEDURE `sp_drop_all_unique_indexes`() BEGIN DECLARE done INT DEFAULT 0; DECLARE v_fk_name VARCHAR(128); DECLARE v_fk_table VARCHAR(128); DECLARE v_ref_table VARCHAR(128); DECLARE v_update VARCHAR(16); DECLARE v_delete VARCHAR(16); DECLARE v_cols TEXT; DECLARE v_ref_cols TEXT; DECLARE v_table VARCHAR(128); DECLARE v_index VARCHAR(128); DECLARE v_index_cols TEXT; DECLARE v_sql TEXT; -- 1) 备份当前库全部外键定义 DROP TEMPORARY TABLE IF EXISTS `_tmp_fk_backup`; CREATE TEMPORARY TABLE `_tmp_fk_backup` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `constraint_name` VARCHAR(128) NOT NULL, `table_name` VARCHAR(128) NOT NULL, `column_names` TEXT NOT NULL, `ref_table_name` VARCHAR(128) NOT NULL, `ref_column_names` TEXT NOT NULL, `update_rule` VARCHAR(16) NOT NULL, `delete_rule` VARCHAR(16) NOT NULL ); INSERT INTO `_tmp_fk_backup` ( `constraint_name`, `table_name`, `column_names`, `ref_table_name`, `ref_column_names`, `update_rule`, `delete_rule` ) SELECT kcu.CONSTRAINT_NAME, kcu.TABLE_NAME, GROUP_CONCAT(CONCAT('`', kcu.COLUMN_NAME, '`') ORDER BY kcu.ORDINAL_POSITION SEPARATOR ','), kcu.REFERENCED_TABLE_NAME, GROUP_CONCAT(CONCAT('`', kcu.REFERENCED_COLUMN_NAME, '`') ORDER BY kcu.ORDINAL_POSITION SEPARATOR ','), rc.UPDATE_RULE, rc.DELETE_RULE FROM information_schema.KEY_COLUMN_USAGE kcu INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS rc ON rc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA AND rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND rc.TABLE_NAME = kcu.TABLE_NAME WHERE kcu.TABLE_SCHEMA = DATABASE() AND kcu.REFERENCED_TABLE_NAME IS NOT NULL GROUP BY kcu.CONSTRAINT_NAME, kcu.TABLE_NAME, kcu.REFERENCED_TABLE_NAME, rc.UPDATE_RULE, rc.DELETE_RULE; -- 2) 删除全部外键(解除对唯一索引的占用) BEGIN DECLARE cur_fk_drop CURSOR FOR SELECT `constraint_name`, `table_name` FROM `_tmp_fk_backup` ORDER BY `id`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; SET done = 0; OPEN cur_fk_drop; fk_drop_loop: LOOP FETCH cur_fk_drop INTO v_fk_name, v_fk_table; IF done = 1 THEN LEAVE fk_drop_loop; END IF; SET v_sql = CONCAT('ALTER TABLE `', v_fk_table, '` DROP FOREIGN KEY `', v_fk_name, '`'); SET @ddl := v_sql; PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END LOOP; CLOSE cur_fk_drop; END; -- 3) 唯一索引 → 同名普通 INDEX(去掉唯一性,保留列与查询能力,便于外键重建) BEGIN DECLARE cur_uk CURSOR FOR SELECT s.TABLE_NAME, s.INDEX_NAME, GROUP_CONCAT(CONCAT('`', s.COLUMN_NAME, '`') ORDER BY s.SEQ_IN_INDEX SEPARATOR ',') FROM information_schema.STATISTICS s WHERE s.TABLE_SCHEMA = DATABASE() AND s.NON_UNIQUE = 0 AND s.INDEX_NAME <> 'PRIMARY' GROUP BY s.TABLE_NAME, s.INDEX_NAME ORDER BY s.TABLE_NAME, s.INDEX_NAME; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; SET done = 0; OPEN cur_uk; uk_loop: LOOP FETCH cur_uk INTO v_table, v_index, v_index_cols; IF done = 1 THEN LEAVE uk_loop; END IF; SET v_sql = CONCAT( 'ALTER TABLE `', v_table, '` DROP INDEX `', v_index, '`, ADD INDEX `', v_index, '` (', v_index_cols, ')' ); SET @ddl := v_sql; PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END LOOP; CLOSE cur_uk; END; -- 4) 按备份重建外键 -- 若原外键引用的是「非主键唯一键」,去掉唯一性后 InnoDB 可能拒绝重建,将跳过并写入警告表 DROP TEMPORARY TABLE IF EXISTS `_tmp_fk_restore_fail`; CREATE TEMPORARY TABLE `_tmp_fk_restore_fail` ( `constraint_name` VARCHAR(128) NOT NULL, `table_name` VARCHAR(128) NOT NULL, `error_msg` TEXT ); BEGIN DECLARE cur_fk_add CURSOR FOR SELECT `constraint_name`, `table_name`, `column_names`, `ref_table_name`, `ref_column_names`, `update_rule`, `delete_rule` FROM `_tmp_fk_backup` ORDER BY `id`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN GET DIAGNOSTICS CONDITION 1 @fk_err = MESSAGE_TEXT; INSERT INTO `_tmp_fk_restore_fail` (`constraint_name`, `table_name`, `error_msg`) VALUES (v_fk_name, v_fk_table, @fk_err); END; SET done = 0; OPEN cur_fk_add; fk_add_loop: LOOP FETCH cur_fk_add INTO v_fk_name, v_fk_table, v_cols, v_ref_table, v_ref_cols, v_update, v_delete; IF done = 1 THEN LEAVE fk_add_loop; END IF; SET v_sql = CONCAT( 'ALTER TABLE `', v_fk_table, '` ADD CONSTRAINT `', v_fk_name, '` FOREIGN KEY (', v_cols, ') REFERENCES `', v_ref_table, '` (', v_ref_cols, ') ON UPDATE ', v_update, ' ON DELETE ', v_delete ); SET @ddl := v_sql; PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END LOOP; CLOSE cur_fk_add; END; -- 输出重建失败的外键(通常因引用侧不再是 UNIQUE/PRIMARY) SELECT * FROM `_tmp_fk_restore_fail`; DROP TEMPORARY TABLE IF EXISTS `_tmp_fk_backup`; DROP TEMPORARY TABLE IF EXISTS `_tmp_fk_restore_fail`; END$$ DELIMITER ; CALL `sp_drop_all_unique_indexes`(); DROP PROCEDURE IF EXISTS `sp_drop_all_unique_indexes`; -- ---------- 校验:唯一索引应无结果(PRIMARY 除外)---------- SELECT TABLE_NAME, INDEX_NAME, GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS columns FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND NON_UNIQUE = 0 AND INDEX_NAME <> 'PRIMARY' GROUP BY TABLE_NAME, INDEX_NAME ORDER BY TABLE_NAME, INDEX_NAME;