关于mysql:强制InnoDB重新检查表/表上的外键?

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

我有一组InnoDB表,我经常需要通过删除一些行并插入其他行来维护这些表。其中一些表具有引用其他表的外键约束,因此这意味着表加载顺序很重要。要插入新行而不必担心表的顺序,我使用:

1
SET FOREIGN_KEY_CHECKS=0;

之前,然后:

1
SET FOREIGN_KEY_CHECKS=1;

后。

加载完成后,我想检查更新表中的数据是否仍然保持参照完整性 - 新行不会破坏外键约束 - 但似乎没有办法做到这一点。

作为测试,我输入的数据确实违反了外键约束,并且在重新启用外键检查时,mysql没有产生任何警告或错误。

如果我试图找到一种方法来指定表加载顺序,并在加载过程中保留外键检查,这将不允许我在具有自引用外键约束的表中加载数据,所以这样做不是一个可接受的解决方案

有没有办法强制InnoDB验证表或数据库的外键约束?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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;

您可以使用此存储过程检查所有数据库中是否有无效的外键。
结果将加载到INVALID_FOREIGN_KEYS表中。
参数ANALYZE_INVALID_FOREIGN_KEYS

  • 数据库名称模式(LIKE样式)
  • 表名模式(LIKE风格)
  • 结果是否是暂时的。它可以是:'Y''N'NULL

    • 'Y'的情况下,ANALYZE_INVALID_FOREIGN_KEYS结果表将是临时表。
      其他会话不会显示临时表。
      您可以与临时结果表并行执行多个ANALYZE_INVALID_FOREIGN_KEYS(...)存储过程。
    • 但是,如果您对其他会话的部分结果感兴趣,则必须使用'N',然后从其他会话执行SELECT * FROM INVALID_FOREIGN_KEYS;
    • 您必须使用NULL跳过事务中的结果表创建,因为MySQL在CREATE TABLE ...DROP TABLE ...的事务中执行隐式提交,因此创建结果表会导致事务中出现问题。在这种情况下,您必须自己从BEGIN; COMMIT/ROLLBACK;块创建结果表:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      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)
      );

      访问有关隐式提交的MySQL站点:http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html

  • INVALID_FOREIGN_KEYS行将仅包含无效数据库,表,列的名称。但是如果有的话,你可以看到执行INVALID_KEY_SQLINVALID_FOREIGN_KEYS的无效引用行。

    如果引用列(也称为外部索引)和引用列(通常是主键)上有索引,则此存储过程将非常快。


    感谢这个伟大的答案 - 这是一个非常方便的工具。这是一个稍微修改过程的版本,在输出表中包含SQL以删除带有无效键的键 - 对于确认这些行只是来自丢失/禁用的孤立删除级联规则的情况(而不是来自主要的孤儿)的情况很方便关键变化或其他更复杂的情况)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    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;


    我修改了脚本来处理多列外键。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    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

    相同的检查,但无效的UNIQUE键分析:

    - >小错误/功能:它也会报告重复的空值。 (虽然mysql允许重复的空值)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    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;

    没有工具可以做到这一点。但是你可以编写一个脚本,它将遍历所有表,删除并重新创建外键约束。在娱乐方面,如果出现问题,将会出现错误。