Check for violated foreign key constraints

Developed In: SQL — Contributed by: Roland Bouman

Foreign key constraint checking in MySQL can be enabled or disabled through the dynamic system variable FOREIGN_KEY_CHECKS. (see: http://dev.mysql.com/doc/refman/5.0/en/set-option.html).

Foreign key constraint checking is sometimes temporarily disabled to make it easier (and faster) to load data into a couple of related tables. Disabling foreign key checking can also be used to emulate DEFERRED foreign key constraints. Re-enabling foreign key checking does not automatically validate the constraint. 'Bad' data may have entered the database during a period of time when foreign key constraint checking was disabled.

This procedure checks for these violations and reports helpful information that may aid in restoring the database's referntial integrity. When a problem is found, a statement is suggested that retrieves the violating rows.

The procedure accepts two parameters. The p_schema_name_pattern parameter accepts a pattern that is used in a LIKE expression to identify the schemas where will be looked. If it is NULL, '%' is assumed. The p_table_name_pattern parameter identifies the tables that will be checked in the schemas identified by the p_schema_name_pattern parameter. The p_table_name_pattern parameter accepts a pattern that is used in a LIKE expression. If it is NULL, '%' is assumed.

Sample usage:

use sakila;

mysql> call p_check_fk_constraint_violations('sakila','%');
+------------------------------------------------------------------------+
| message                                                                |
+------------------------------------------------------------------------+
| Checked 22 foreign keys, found 0 violated and a total of 0 violations. |
+------------------------------------------------------------------------+
1 row in set (0.51 sec)


mysql> set foreign_key_checks = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from sakila.film;
Query OK, 1000 rows affected (0.62 sec)

mysql> call p_check_fk_constraint_violations('sakila','%');
+------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------+
| message
                                                           |
+------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------+
| 5462 violations found for foreign key `fk_film_actor_film` on table `film_actor` in schema `sakila`.
Violating set can be retrieved with the following statement:

select      *
from        `sakila`.`film_actor` c
left join   `sakila`.`film` p
on          c.film_id=p.film_id
where       p.film_id is null;
 |
+------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------+
1 row in set (0.53 sec)

+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------+
| message
                                                                    |
+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------+
| 1000 violations found for foreign key `fk_film_category_film` on table `film_category` in schema `sakila`.
Violating set can be retrieved with the following statement:

select      *
from        `sakila`.`film_category` c
left join   `sakila`.`film` p
on          c.film_id=p.film_id
where       p.film_id is null;
 |
+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------+
1 row in set (0.53 sec)

+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------+
| message
                                                        |
+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------+
| 4581 violations found for foreign key `fk_inventory_film` on table `inventory` in schema `sakila`.
Violating set can be retrieved with the following statement:

select      *
from        `sakila`.`inventory` c
left join   `sakila`.`film` p
on          c.film_id=p.film_id
where       p.film_id is null;
 |
+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------+
1 row in set (0.53 sec)

+----------------------------------------------------------------------------+
| message                                                                    |
+----------------------------------------------------------------------------+
| Checked 22 foreign keys, found 3 violated and a total of 11043 violations. |
+----------------------------------------------------------------------------+

Roland Bouman
SQL
  1. delimiter //
  2.  
  3. DROP procedure IF EXISTS p_check_fk_constraint_violations
  4. //
  5.  
  6. CREATE procedure p_check_fk_constraint_violations(
  7. p_schema_name_pattern varchar(64)
  8. , p_table_name_pattern varchar(64)
  9. )
  10. reads sql DATA
  11. sql security invoker
  12. comment 'Finds violated Foreign Key Constaints.'
  13. begin
  14. -- current schema name
  15. declare v_schema_name varchar(64)
  16. DEFAULT coalesce(p_schema_name_pattern,'%');
  17. -- current table name
  18. declare v_table_name varchar(64)
  19. DEFAULT coalesce(p_table_name_pattern,'%');
  20. -- current foreign key constraint name
  21. declare v_constraint_name varchar(64);
  22. -- SQL SELECT-expression that retrieves the violating set
  23. declare v_statement_text text;
  24. -- number of foreign key constraints checked so far
  25. declare v_count_constraints int UNSIGNED
  26. DEFAULT 0;
  27. -- number of foreign key constraints found to be violated so far
  28. declare v_count_violated_constraints int UNSIGNED
  29. DEFAULT 0;
  30. -- number of violating records found so far
  31. declare v_count_violations int UNSIGNED
  32. DEFAULT 0;
  33. -- cursor loop constrol variable
  34. declare v_done bit DEFAULT false;
  35. -- This cursor yields one row per foreign key constraint.
  36. -- Only foreign keys found in tables specified by the
  37. -- p_schema_name_pattern and p_table_name_pattern
  38. -- are checked.
  39.  
  40. declare csr_fk_constraint
  41. cursor FOR
  42. SELECT con.table_schema
  43. , con.table_name
  44. , con.constraint_name
  45. , concat(
  46. 'from `'
  47. , con.table_schema
  48. , '`.`'
  49. , con.table_name
  50. , '` c'
  51. ,'\n','left join '
  52. , max(
  53. concat(
  54. '`'
  55. , concol.referenced_table_schema
  56. , '`.`'
  57. , concol.referenced_table_name
  58. , '`'
  59. )
  60. )
  61. , ' p'
  62. ,'\n','on '
  63. , group_concat(
  64. concat(
  65. 'c.'
  66. , concol.column_name
  67. , '='
  68. , 'p.'
  69. , concol.referenced_column_name
  70. )
  71. ORDER BY concol.ordinal_position
  72. separator '\nand '
  73. )
  74. ,'\n','where p.'
  75. , max(concol.referenced_column_name)
  76. , ' is null'
  77. , IF (max(col.is_nullable)='YES'
  78. , group_concat(
  79. IF( col.is_nullable = 'YES'
  80. , concat(
  81. '\nand c.'
  82. , col.column_name
  83. , ' is not null'
  84. )
  85. , ''
  86. )
  87. )
  88. , ''
  89. )
  90. ) AS statement_text
  91. FROM information_schema.table_constraints con
  92. INNER JOIN information_schema.key_column_usage concol
  93. ON con.constraint_schema = concol.constraint_schema
  94. AND con.constraint_name = concol.constraint_name
  95. INNER JOIN information_schema.COLUMNS col
  96. ON concol.table_schema = col.table_schema
  97. AND concol.table_name = col.table_name
  98. AND concol.column_name = col.column_name
  99. WHERE con.table_schema LIKE v_schema_name
  100. AND con.table_name LIKE v_table_name
  101. AND con.constraint_type = 'FOREIGN KEY'
  102. GROUP BY con.table_schema
  103. , con.table_name
  104. , con.constraint_name
  105. ;
  106. declare continue handler FOR NOT found
  107. SET v_done := true
  108. ;
  109.  
  110. open csr_fk_constraint
  111. ;
  112. fk_loop: loop
  113. fetch csr_fk_constraint
  114. INTO v_schema_name
  115. , v_table_name
  116. , v_constraint_name
  117. , v_statement_text
  118. ;
  119. IF v_done then
  120. leave fk_loop
  121. ;
  122. end IF
  123. ;
  124. SET @statement_text := concat(
  125. 'set @count_fk_violations := '
  126. , '(select count(1) ',v_statement_text,')'
  127. );
  128. prepare stmt FROM @statement_text
  129. ;
  130. SET @count_fk_violations := 0
  131. ;
  132. execute stmt
  133. ;
  134. deallocate prepare stmt
  135. ;
  136. SET v_count_constraints := v_count_constraints + 1
  137. ;
  138. IF @count_fk_violations != 0 then
  139. SET v_count_violated_constraints := v_count_violated_constraints + 1
  140. ;
  141. SET v_count_violations := v_count_violations + @count_fk_violations
  142. ;
  143. SELECT concat(
  144. @count_fk_violations
  145. , ' violations found for foreign key '
  146. , '`',v_constraint_name,'`'
  147. , ' on table '
  148. , '`',v_table_name,'`'
  149. , ' in schema '
  150. , '`',v_schema_name,'`.'
  151. , '\n'
  152. , 'Violating set can be retrieved with the following statement:'
  153. , '\n\n'
  154. , 'select c.*'
  155. , '\n'
  156. , v_statement_text
  157. , ';\n'
  158. ) AS message;
  159.  
  160. end IF;
  161. end loop;
  162. SELECT concat(
  163. 'Checked '
  164. , v_count_constraints
  165. , ' foreign keys, found '
  166. , v_count_violated_constraints
  167. , ' violated and a total of '
  168. , v_count_violations
  169. , ' violations.'
  170. ) AS message;
  171. end;
  172. //
  173.  

Current Tags

You must be logged in to tag this tool

nice code, hopefully i found it before i write a similar tool myself. you save me a lot a time, thx

Thanks exactly what i was looking for> 6S

In order to know the foreign key relationships:

  1. SELECT NULL AS PKTABLE_CAT, A.REFERENCED_TABLE_SCHEMA AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_CATALOG AS FKTABLE_CAT, A.TABLE_SCHEMA AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME, A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ, NULL AS UPDATE_RULE, NULL AS DELETE_RULE, A.CONSTRAINT_NAME AS FK_NAME, NULL AS PK_NAME, NULL AS DEFERABILITY, NULL AS UNIQUE_OR_PRIMARY
  2. FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A, INFORMATION_SCHEMA.TABLE_CONSTRAINTS B WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA AND A.TABLE_NAME = B.TABLE_NAME AND A.CONSTRAINT_NAME = B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL HAVING PKTABLE_SCHEM IS NOT NULL ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION LIMIT 10;
  3.  
  4. SELECT A.TABLE_SCHEMA AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME, A.COLUMN_NAME AS FKCOLUMN_NAME, A.REFERENCED_TABLE_SCHEMA AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.CONSTRAINT_NAME AS FK_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A, INFORMATION_SCHEMA.TABLE_CONSTRAINTS B WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA AND A.TABLE_NAME = B.TABLE_NAME AND A.CONSTRAINT_NAME = B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL HAVING PKTABLE_SCHEM IS NOT NULL ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION LIMIT 100;

Votes

  • Rated 5.00 out of 5
Rated 5.00 out of 5 with 2 votes cast.
You must be logged in to vote.

Watches

0 members are watching this tool
You must be logged in to track this tool.

Provide Feedback

Please note:
HTML will be purified, but we allow for a number of HTML tags so that you have the flexibility to decorate your comment text to some extent. The comments allow the following HTML tags:

strong, b, em, blockquote, a, code, pre

To put code into your comment, simply encapsulate your code with
[code language="XXX"][/code], where XXX is any common language, for instance "PHP", "SQL", "C", etc.



You must be logged in to comment