Forzar InnoDB para volver a verificar claves externas en una tabla/tablas?


52

Tengo un conjunto de tablas InnoDB que periódicamente necesito mantener eliminando algunas filas e insertando otras. Varias de las tablas tienen restricciones de clave externa que hacen referencia a otras tablas, por lo que significa que el orden de carga de la tabla es importante. Para insertar las nuevas filas sin preocuparse por el orden de las tablas, que utilizo:

SET FOREIGN_KEY_CHECKS=0; 

antes, y luego:

SET FOREIGN_KEY_CHECKS=1; 

después.

Cuando se completa la carga, me gustaría comprobar que los datos en las tablas actualizadas todavía tienen integridad referencial, que las nuevas filas no rompen las restricciones de clave externa, pero parece que no hay forma de hacer esto.

Como una prueba, ingresé datos que estaba seguro violaron restricciones de clave externa, y al volver a habilitar las comprobaciones de clave externa, mysql no produjo advertencias o errores.

Si traté de encontrar una forma de especificar el orden de carga de la tabla y dejé las claves externas durante el proceso de carga, esto no me permitió cargar datos en una tabla que tiene una restricción de clave externa autorreferencial , entonces esta no sería una solución aceptable.

¿Hay alguna manera de forzar InnoDB para verificar las claves foráneas de una base de datos de una tabla o?

87
DELIMITER $$ 

DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS$$ 

CREATE 
    PROCEDURE `ANALYZE_INVALID_FOREIGN_KEYS`(
     checked_database_name VARCHAR(64), 
     checked_table_name VARCHAR(64), 
     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 KEYS_SQL_VAR VARCHAR(1024); 

     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; 


     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; 


      SET @from_part = CONCAT('FROM ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', ' AS REFERRING ', 
       'LEFT JOIN `', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', ' AS REFERRED ', 
       'ON (REFERRING', '.`', COLUMN_NAME_VAR, '`', ' = ', 'REFERRED', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ') ', 
       'WHERE REFERRING', '.`', COLUMN_NAME_VAR, '`', ' IS NOT NULL ', 
       'AND REFERRED', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ' IS NULL'); 
      SET @full_query = CONCAT('SELECT COUNT(*) ', @from_part, ' INTO @invalid_key_count;'); 
      PREPARE stmt FROM @full_query; 

      EXECUTE stmt; 
      IF @invalid_key_count > 0 THEN 
       INSERT INTO 
        INVALID_FOREIGN_KEYS 
       SET 
        `TABLE_SCHEMA` = TABLE_SCHEMA_VAR, 
        `TABLE_NAME` = TABLE_NAME_VAR, 
        `COLUMN_NAME` = COLUMN_NAME_VAR, 
        `CONSTRAINT_NAME` = CONSTRAINT_NAME_VAR, 
        `REFERENCED_TABLE_SCHEMA` = REFERENCED_TABLE_SCHEMA_VAR, 
        `REFERENCED_TABLE_NAME` = REFERENCED_TABLE_NAME_VAR, 
        `REFERENCED_COLUMN_NAME` = REFERENCED_COLUMN_NAME_VAR, 
        `INVALID_KEY_COUNT` = @invalid_key_count, 
        `INVALID_KEY_SQL` = CONCAT('SELECT ', 
         'REFERRING.', '`', COLUMN_NAME_VAR, '` ', 'AS "Invalid: ', COLUMN_NAME_VAR, '", ', 
         'REFERRING.* ', 
         @from_part, ';'); 
      END IF; 
      DEALLOCATE PREPARE stmt; 

     END LOOP foreign_key_cursor_loop; 
    END$$ 

DELIMITER ; 

CALL ANALYZE_INVALID_FOREIGN_KEYS('%', '%', 'Y'); 
DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS; 

SELECT * FROM INVALID_FOREIGN_KEYS; 

Puede utilizar este procedimiento almacenado para comprobar toda la base de datos de claves externas no válidos. El resultado se cargará en la tabla INVALID_FOREIGN_KEYS. Parámetros de ANALYZE_INVALID_FOREIGN_KEYS:

  1. patrón de nombre de base de datos (como el estilo) patrón
  2. Nombre de la tabla (como el estilo)
  3. Si el resultado será temporal. Puede ser: 'Y', 'N', NULL.

    • En caso de 'Y' la tabla de resultados ANALYZE_INVALID_FOREIGN_KEYS habrá tabla temporal. La tabla temporal no será visible para otras sesiones. Se pueden ejecutar múltiples ANALYZE_INVALID_FOREIGN_KEYS(...) procedimiento almacenado en paralelo con la tabla de resultados temporal.
    • Pero si usted está interesado en el resultado parcial de una sesión de otra, entonces usted debe utilizar 'N', a continuación, ejecutar SELECT * FROM INVALID_FOREIGN_KEYS; desde otra sesión.
    • Debe omitir la creación de la tabla de resultados en la transacción, porque MySQL ejecuta la confirmación implícita en la transacción CREATE TABLE ... y , por lo que la creación de la tabla de resultados podría causar problemas en la transacción.En este caso se debe crear la tabla de resultados usted mismo fuera de BEGIN; COMMIT/ROLLBACK; bloque:

      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) 
      ); 
      

      Visita el sitio de MySQL sobre COMMIT implícito: http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html

Los INVALID_FOREIGN_KEYS filas contendrá únicamente el nombre del inválida base de datos, tabla, columna. Pero puede ver las filas de referencia no válidas con la ejecución del valor de la columna INVALID_KEY_SQL de INVALID_FOREIGN_KEYS, si las hay.

Este procedimiento almacenado será muy rápido si hay índices en las columnas de referencia (también conocido como índice externo) y en las columnas referidas (generalmente, clave principal).

  0

Great answer. Bienvenido a Stack Overflow! 12 may. 112011-05-12 12:45:37

+3

Encontré que tenía que agregar 'collate utf8_general_ci' después de los parámetros checked_ * en la cláusula CURSORS WHERE porque mi esquema de información usa utf8_general_ci mientras que la cuenta de inicio de sesión usa el utf8_unicode_ci más correcto. Sin esto recibí un error de desajuste de colación en la cláusula similar. "DONDE' CONSTRAINT_SCHEMA' ME GUSTA checked_database_name collate utf8_general_ci Y 'TABLE_NAME' COMO LIKE_table_name collate utf8_general_ci AND' REFERENCED_TABLE_SCHEMA' IS NOT NULL; 14 ago. 132013-08-14 13:40:47


1

No hay herramienta, eso puede hacer eso. Pero puede escribir un script que recorra todas sus tablas, elimine y vuelva a crear restricciones de clave externa. En recreación, habrá un error si algo está mal.


15

Gracias por esta gran respuesta, esta es una herramienta muy útil. Aquí hay una versión ligeramente modificada del procedimiento que incluye SQL en la tabla de salida para eliminar claves con claves no válidas, útil para los casos en los que ha confirmado que estas filas son simplemente huérfanos de reglas de cascada de eliminación faltantes o inhabilitadas (y no huérfanos de primaria cambios clave u otros casos más complejos).

DELIMITER $$ 

DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS$$ 

CREATE 
    PROCEDURE `ANALYZE_INVALID_FOREIGN_KEYS`(
     checked_database_name VARCHAR(64), 
     checked_table_name VARCHAR(64), 
     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 KEYS_SQL_VAR VARCHAR(1024); 

     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), 
       `INVALID_KEY_DELETE_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), 
       `INVALID_KEY_DELETE_SQL` VARCHAR(1024) 
      ); 
     END IF; 


     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; 


      SET @from_part = CONCAT('FROM ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', ' AS REFERRING ', 
       'LEFT JOIN `', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', ' AS REFERRED ', 
       'ON (REFERRING', '.`', COLUMN_NAME_VAR, '`', ' = ', 'REFERRED', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ') ', 
       'WHERE REFERRING', '.`', COLUMN_NAME_VAR, '`', ' IS NOT NULL ', 
       'AND REFERRED', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ' IS NULL'); 
      SET @full_query = CONCAT('SELECT COUNT(*) ', @from_part, ' INTO @invalid_key_count;'); 
      PREPARE stmt FROM @full_query; 

      EXECUTE stmt; 
      IF @invalid_key_count > 0 THEN 
       INSERT INTO 
        INVALID_FOREIGN_KEYS 
       SET 
        `TABLE_SCHEMA` = TABLE_SCHEMA_VAR, 
        `TABLE_NAME` = TABLE_NAME_VAR, 
        `COLUMN_NAME` = COLUMN_NAME_VAR, 
        `CONSTRAINT_NAME` = CONSTRAINT_NAME_VAR, 
        `REFERENCED_TABLE_SCHEMA` = REFERENCED_TABLE_SCHEMA_VAR, 
        `REFERENCED_TABLE_NAME` = REFERENCED_TABLE_NAME_VAR, 
        `REFERENCED_COLUMN_NAME` = REFERENCED_COLUMN_NAME_VAR, 
        `INVALID_KEY_COUNT` = @invalid_key_count, 
        `INVALID_KEY_SQL` = CONCAT('SELECT ', 
         'REFERRING.', '`', COLUMN_NAME_VAR, '` ', 'AS "Invalid: ', COLUMN_NAME_VAR, '", ', 
         'REFERRING.* ', 
         @from_part, ';'), 
        `INVALID_KEY_DELETE_SQL` = CONCAT('DELETE ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '` ', 
         'FROM ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', ' ', 
         'LEFT JOIN `', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', ' ', 
         'ON (', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', '.`', COLUMN_NAME_VAR, '`', ' = ', '`', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ') ', 
         'WHERE ', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', '.`', COLUMN_NAME_VAR, '`', ' IS NOT NULL ', 
         'AND ', '`', REFERENCED_TABLE_SCHEMA_VAR, '`.`', REFERENCED_TABLE_NAME_VAR, '`', '.`', REFERENCED_COLUMN_NAME_VAR, '`', ' IS NULL', ';'); 
      END IF; 
      DEALLOCATE PREPARE stmt; 

     END LOOP foreign_key_cursor_loop; 
    END$$ 

DELIMITER ; 

CALL ANALYZE_INVALID_FOREIGN_KEYS('%', '%', 'Y'); 
DROP PROCEDURE IF EXISTS ANALYZE_INVALID_FOREIGN_KEYS; 

SELECT * FROM INVALID_FOREIGN_KEYS; 
+2

Sé que es viejo ahora pero me encontré con esto y me pregunto: cualquier razón por la que no estés usando 'CONCAT ('DELETE REFERING', @from_part, ';') 'para la declaración DELETE? 18 may. 152015-05-18 12:17:51


1

El mismo cheque, pero para el análisis de las claves únicas válido:

->Pequeño bug/función: Se informará también nulos duplicados. (Mientras que mysql permite nulos duplicados).

DELIMITER $$ 

DROP PROCEDURE IF EXISTS ANALYZE_INVALID_UNIQUE_KEYS$$ 

CREATE 
    PROCEDURE `ANALYZE_INVALID_UNIQUE_KEYS`(
     checked_database_name VARCHAR(64), 
     checked_table_name VARCHAR(64)) 

    LANGUAGE SQL 
    NOT DETERMINISTIC 
    READS SQL DATA 

    BEGIN 
     DECLARE TABLE_SCHEMA_VAR VARCHAR(64); 
     DECLARE TABLE_NAME_VAR VARCHAR(64); 
     DECLARE COLUMN_NAMES_VAR VARCHAR(1000); 
     DECLARE CONSTRAINT_NAME_VAR VARCHAR(64); 

     DECLARE done INT DEFAULT 0; 

     DECLARE unique_key_cursor CURSOR FOR 
      select kcu.table_schema sch, 
        kcu.table_name tbl, 
        group_concat(kcu.column_name) colName, 
        kcu.constraint_name constName 
      from 
       information_schema.table_constraints tc 
      join 
       information_schema.key_column_usage kcu 
      on 
       kcu.constraint_name=tc.constraint_name 
       and kcu.constraint_schema=tc.constraint_schema 
       and kcu.table_name=tc.table_name 
      where 
       kcu.table_schema like checked_database_name 
       and kcu.table_name like checked_table_name 
       and tc.constraint_type="UNIQUE" group by sch, tbl, constName; 

     DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 

     DROP TEMPORARY TABLE IF EXISTS INVALID_UNIQUE_KEYS; 
     CREATE TEMPORARY TABLE INVALID_UNIQUE_KEYS(
      `TABLE_SCHEMA` VARCHAR(64), 
      `TABLE_NAME` VARCHAR(64), 
      `COLUMN_NAMES` VARCHAR(1000), 
      `CONSTRAINT_NAME` VARCHAR(64), 
      `INVALID_KEY_COUNT` INT 
     ); 



     OPEN unique_key_cursor; 
     unique_key_cursor_loop: LOOP 
      FETCH unique_key_cursor INTO 
      TABLE_SCHEMA_VAR, 
      TABLE_NAME_VAR, 
      COLUMN_NAMES_VAR, 
      CONSTRAINT_NAME_VAR; 
      IF done THEN 
       LEAVE unique_key_cursor_loop; 
      END IF; 

      SET @from_part = CONCAT('FROM (SELECT COUNT(*) counter FROM', '`', TABLE_SCHEMA_VAR, '`.`', TABLE_NAME_VAR, '`', 
        ' GROUP BY ', COLUMN_NAMES_VAR , ') as s where s.counter > 1'); 
      SET @full_query = CONCAT('SELECT COUNT(*) ', @from_part, ' INTO @invalid_key_count;'); 
      PREPARE stmt FROM @full_query; 
      EXECUTE stmt; 
      IF @invalid_key_count > 0 THEN 
       INSERT INTO 
        INVALID_UNIQUE_KEYS 
       SET 
        `TABLE_SCHEMA` = TABLE_SCHEMA_VAR, 
        `TABLE_NAME` = TABLE_NAME_VAR, 
        `COLUMN_NAMES` = COLUMN_NAMES_VAR, 
        `CONSTRAINT_NAME` = CONSTRAINT_NAME_VAR, 
        `INVALID_KEY_COUNT` = @invalid_key_count; 
      END IF; 
      DEALLOCATE PREPARE stmt; 

     END LOOP unique_key_cursor_loop; 
    END$$ 

DELIMITER ; 

CALL ANALYZE_INVALID_UNIQUE_KEYS('%', '%'); 
DROP PROCEDURE IF EXISTS ANALYZE_INVALID_UNIQUE_KEYS; 

SELECT * FROM INVALID_UNIQUE_KEYS; 

1

Modifiqué la secuencia de comandos para manejar varias columnas claves foráneas.

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 
    SQL SECURITY DEFINER 
    COMMENT '' 
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 KEYS_SQL_VAR VARCHAR(1024); 

    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_name = ''; 
    SET @from_part = ''; 
    SET @where_part = ''; 
    SET @where_nullable = ''; 

    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_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` = TABLE_SCHEMA_VAR, 
        `TABLE_NAME` = TABLE_NAME_VAR, 
        `COLUMN_NAME` = NULL, 
        `CONSTRAINT_NAME` = CONSTRAINT_NAME_VAR, 
        `REFERENCED_TABLE_SCHEMA` = REFERENCED_TABLE_SCHEMA_VAR, 
        `REFERENCED_TABLE_NAME` = REFERENCED_TABLE_NAME_VAR, 
        `REFERENCED_COLUMN_NAME` = NULL, 
        `INVALID_KEY_COUNT` = @invalid_key_count, 
        `INVALID_KEY_SQL` = @invalid_query; 
      END IF; 
      DEALLOCATE PREPARE stmt; 

      SET @where_part = ''; 
      SET @where_nullable = ''; 
     END IF; 

     IF (LENGTH(@where_part) > 0) THEN 
      SET @where_nullable = CONCAT(@where_nullable, ' OR '); 
      SET @where_part = CONCAT(@where_part, ' AND '); 
     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 '); 
     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_name = CONSTRAINT_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` = TABLE_SCHEMA_VAR, 
       `TABLE_NAME` = TABLE_NAME_VAR, 
       `COLUMN_NAME` = NULL, 
       `CONSTRAINT_NAME` = CONSTRAINT_NAME_VAR, 
       `REFERENCED_TABLE_SCHEMA` = REFERENCED_TABLE_SCHEMA_VAR, 
       `REFERENCED_TABLE_NAME` = REFERENCED_TABLE_NAME_VAR, 
       `REFERENCED_COLUMN_NAME` = NULL, 
       `INVALID_KEY_COUNT` = @invalid_key_count, 
       `INVALID_KEY_SQL` = @invalid_query; 
     END IF; 
     DEALLOCATE PREPARE stmt; 
    END IF; 
END