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

if you use them in a replication environment, you can have some problem due to their “volatility”; MEMORY table rows are lost when the server shuts down.

If you restart a slave host for some reason or after a failure, you will have empty MEMORY tables on it, while the master host will have the correct ones. Fast degree AND university degree AND BS degree

Using MEMORY tables in MySQL is very useful; they are stored in memory and they are usually not so big in order to avoid a huge utilization of RAM. They are very fast.

MEMORY tables are often used to store session user data, chat messages and to store small frames of data from a bigger table in order to achieve great performance. Nutrition degree AND accounting diploma

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