Force InnoDB to recheck foreign keys on a table/tables?

后端 未结 5 904
暖寄归人
暖寄归人 2020-11-28 18:34

I have a set of InnoDB tables that I periodically need to maintain by removing some rows and inserting others. Several of the tables have foreign key constra

5条回答
  •  野性不改
    2020-11-28 19:32

    I modified the script to handle multiple columns foreign keys.

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS$$
    
    CREATE PROCEDURE `ANALYZE_INVALID_FOREIGN_KEYS`(
        IN `checked_database_name` VARCHAR(64),
        IN `checked_table_name` VARCHAR(64),
        IN `temporary_result_table` ENUM('Y', 'N')
    )
        LANGUAGE SQL
        NOT DETERMINISTIC
        READS SQL DATA
    
    BEGIN
        DECLARE TABLE_SCHEMA_VAR VARCHAR(64);
        DECLARE TABLE_NAME_VAR VARCHAR(64);
        DECLARE COLUMN_NAME_VAR VARCHAR(64); 
        DECLARE CONSTRAINT_NAME_VAR VARCHAR(64);
        DECLARE REFERENCED_TABLE_SCHEMA_VAR VARCHAR(64);
        DECLARE REFERENCED_TABLE_NAME_VAR VARCHAR(64);
        DECLARE REFERENCED_COLUMN_NAME_VAR VARCHAR(64);
    
        DECLARE done INT DEFAULT 0;
    
        DECLARE foreign_key_cursor CURSOR FOR
            SELECT
                `TABLE_SCHEMA`,
                `TABLE_NAME`,
                `COLUMN_NAME`,
                `CONSTRAINT_NAME`,
                `REFERENCED_TABLE_SCHEMA`,
                `REFERENCED_TABLE_NAME`,
                `REFERENCED_COLUMN_NAME`
            FROM 
                information_schema.KEY_COLUMN_USAGE 
            WHERE 
                `CONSTRAINT_SCHEMA` LIKE checked_database_name AND
                `TABLE_NAME` LIKE checked_table_name AND
                `REFERENCED_TABLE_SCHEMA` IS NOT NULL;
    
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
        IF temporary_result_table = 'N' THEN
            DROP TEMPORARY TABLE IF EXISTS INVALID_FOREIGN_KEYS;
            DROP TABLE IF EXISTS INVALID_FOREIGN_KEYS;
    
            CREATE TABLE INVALID_FOREIGN_KEYS(
                `TABLE_SCHEMA` VARCHAR(64), 
                `TABLE_NAME` VARCHAR(64), 
                `COLUMN_NAME` VARCHAR(64), 
                `CONSTRAINT_NAME` VARCHAR(64),
                `REFERENCED_TABLE_SCHEMA` VARCHAR(64),
                `REFERENCED_TABLE_NAME` VARCHAR(64),
                `REFERENCED_COLUMN_NAME` VARCHAR(64),
                `INVALID_KEY_COUNT` INT,
                `INVALID_KEY_SQL` VARCHAR(1024)
            );
        ELSEIF temporary_result_table = 'Y' THEN
            DROP TEMPORARY TABLE IF EXISTS INVALID_FOREIGN_KEYS;
            DROP TABLE IF EXISTS INVALID_FOREIGN_KEYS;
    
            CREATE TEMPORARY TABLE INVALID_FOREIGN_KEYS(
                `TABLE_SCHEMA` VARCHAR(64), 
                `TABLE_NAME` VARCHAR(64), 
                `COLUMN_NAME` VARCHAR(64), 
                `CONSTRAINT_NAME` VARCHAR(64),
                `REFERENCED_TABLE_SCHEMA` VARCHAR(64),
                `REFERENCED_TABLE_NAME` VARCHAR(64),
                `REFERENCED_COLUMN_NAME` VARCHAR(64),
                `INVALID_KEY_COUNT` INT,
                `INVALID_KEY_SQL` VARCHAR(1024)
            );
        END IF;
    
        SET @prev_constraint_name = '';
        SET @prev_table_schema = '';
        SET @prev_table_name = '';
        SET @prev_referenced_table_schema = '';
        SET @prev_referenced_table_name = '';
    
        SET @from_part = '';
        SET @where_part = '';
        SET @where_nullable = '';
    
        SET @all_columns = '';
        SET @all_referenced_columns = '';
    
        OPEN foreign_key_cursor;
        foreign_key_cursor_loop: LOOP               
            FETCH foreign_key_cursor INTO 
            TABLE_SCHEMA_VAR, 
            TABLE_NAME_VAR, 
            COLUMN_NAME_VAR, 
            CONSTRAINT_NAME_VAR, 
            REFERENCED_TABLE_SCHEMA_VAR, 
            REFERENCED_TABLE_NAME_VAR, 
            REFERENCED_COLUMN_NAME_VAR;
    
            IF done THEN
                LEAVE foreign_key_cursor_loop;
            END IF;
    
            IF (@prev_constraint_name <> CONSTRAINT_NAME_VAR AND @from_part <> '' AND @where_part <> '') THEN
    
                SET @full_query = CONCAT('SELECT COUNT(*) ', @from_part, ' WHERE (', @where_nullable , ') AND ', @from_where_part, 'WHERE ', @where_part, ') INTO @invalid_key_count;');
                SET @invalid_query = CONCAT('SELECT * ', @from_part, ' WHERE (', @where_nullable , ') AND ', @from_where_part, 'WHERE ', @where_part, ')');
                PREPARE stmt FROM @full_query;
    
                EXECUTE stmt;
                IF @invalid_key_count > 0 THEN
                    INSERT INTO 
                        INVALID_FOREIGN_KEYS 
                    SET 
                        `TABLE_SCHEMA` = @prev_table_schema, 
                        `TABLE_NAME` = @prev_table_name, 
                        `COLUMN_NAME` = @all_columns, 
                        `CONSTRAINT_NAME` = @prev_constraint_name, 
                        `REFERENCED_TABLE_SCHEMA` = @prev_referenced_table_schema, 
                        `REFERENCED_TABLE_NAME` = @prev_table_name, 
                        `REFERENCED_COLUMN_NAME` = @all_referenced_columns, 
                        `INVALID_KEY_COUNT` = @invalid_key_count,
                        `INVALID_KEY_SQL` = @invalid_query;
                END IF;
                DEALLOCATE PREPARE stmt; 
    
                SET @where_part = '';
                SET @where_nullable = '';
    
                SET @all_columns = '';
                SET @all_referenced_columns = '';
            END IF;
    
            IF (LENGTH(@where_part) > 0) THEN
                SET @where_nullable = CONCAT(@where_nullable, ' OR ');
                SET @where_part = CONCAT(@where_part, ' AND ');
    
                SET @all_columns = CONCAT(@all_columns, ', ' COLUMN_NAME_VAR);
                SET @all_referenced_columns = CONCAT(@all_referenced_columns, ', ' REFERENCED_COLUMN_NAME_VAR);
            ELSE
                SET @from_part = CONCAT('FROM ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', ' AS REFERRING ');
                SET @from_where_part = CONCAT('NOT EXISTS (SELECT * FROM `', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', ' AS REFERRED ');
    
                SET @all_columns = COLUMN_NAME_VAR;
                SET @all_referenced_columns = REFERENCED_COLUMN_NAME_VAR;
            END IF;
    
            SET @where_nullable = CONCAT(@where_nullable, 'REFERRING.', COLUMN_NAME_VAR, ' IS NOT NULL');
            SET @where_part = CONCAT(@where_part, 'REFERRING.', COLUMN_NAME_VAR, ' = ', 'REFERRED.', REFERENCED_COLUMN_NAME_VAR);
    
            SET @prev_constraint_name = CONSTRAINT_NAME_VAR;
            SET @prev_table_schema = TABLE_SCHEMA_VAR;
            SET @prev_table_name = TABLE_NAME_VAR;
            SET @prev_referenced_table_schema = REFERENCED_TABLE_SCHEMA_VAR;
            SET @prev_referenced_table_name = REFERENCED_TABLE_NAME_VAR;
    
        END LOOP foreign_key_cursor_loop;
    
        IF (@where_part <> '' AND @from_part <> '') THEN
    
            SET @full_query = CONCAT('SELECT COUNT(*) ', @from_part, ' WHERE (', @where_nullable , ') AND ', @from_where_part, 'WHERE ', @where_part, ') INTO @invalid_key_count;');
            SET @invalid_query = CONCAT('SELECT * ', @from_part, ' WHERE (', @where_nullable , ') AND ', @from_where_part, 'WHERE ', @where_part, ')');
            PREPARE stmt FROM @full_query;
    
            EXECUTE stmt;
            IF @invalid_key_count > 0 THEN
                INSERT INTO 
                    INVALID_FOREIGN_KEYS 
                SET 
                    `TABLE_SCHEMA` = @prev_table_schema, 
                    `TABLE_NAME` = @prev_table_name, 
                    `COLUMN_NAME` = @all_columns, 
                    `CONSTRAINT_NAME` = @prev_constraint_name, 
                    `REFERENCED_TABLE_SCHEMA` = @prev_referenced_table_schema, 
                    `REFERENCED_TABLE_NAME` = @prev_table_name, 
                    `REFERENCED_COLUMN_NAME` = @all_referenced_columns, 
                    `INVALID_KEY_COUNT` = @invalid_key_count,
                    `INVALID_KEY_SQL` = @invalid_query;
            END IF;
            DEALLOCATE PREPARE stmt; 
        END IF;
    END$$
    
    DELIMITER ;
    
    CALL ANALYZE_INVALID_FOREIGN_KEYS('%', '%', 'Y');
    DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS;
    
    SELECT * FROM INVALID_FOREIGN_KEYS;
    

提交回复
热议问题