Find child tables

from the Artful Common Queries page


Starting with MySQL 5, you can find all tables which have foreign key references to a given table with an information_schema query, here encapsulated in a stored procedure which takes two arguments, a database name and a table name. When the table argument is blank or NULL, the procedure returns all parent-child links where the parent table is in the specified database; otherwise it returns parent-child links for the specified parent table:

DROP PROCEDURE IF EXISTS ListChildren;
DELIMITER |
CREATE PROCEDURE ListChildren( pdb CHAR(64), ptbl CHAR(64) )
BEGIN
  IF ptbl = '' OR ptbl IS NULL THEN
    SELECT 
      c.table_schema as 'Parent Schema',
      u.referenced_table_name as 'Parent Table',
      u.referenced_column_name as 'Parent Column',
      u.table_schema as 'Child Schema',
      u.table_name as 'Child Table',
      u.column_name as 'Child Column'
    FROM information_schema.table_constraints AS c
    JOIN information_schema.key_column_usage AS u USING(constraint_schema,constraint_name)
    WHERE c.constraint_type = 'FOREIGN KEY' 
      AND u.referenced_table_schema = pdb
    ORDER BY 
      c.table_schema, u.referenced_table_name, u.referenced_column_name,
      u.table_schema, u.table_name, u.column_name;
  ELSE
    SELECT 
      c.table_schema as 'Parent Schema',
      u.referenced_table_name as 'Parent Table',
      u.referenced_column_name as 'Parent Column',
      u.table_schema as 'Child Schema',
      u.table_name as 'Child Table',
      u.column_name as 'Child Column'
    FROM information_schema.table_constraints AS c
    JOIN information_schema.key_column_usage AS u USING(constraint_schema,constraint_name)
    WHERE c.constraint_type = 'FOREIGN KEY' 
      AND u.referenced_table_schema = pdb
      AND u.referenced_table_name = ptbl
    ORDER BY 
      c.table_schema, u.referenced_table_name, u.referenced_column_name,
      u.table_schema, u.table_name, u.column_name;
  END IF;
END |
DELIMITER ;

Unfortunately it performs slowly, not because of how it is written, but because of how MySQL has implemented information_schema. Indeed a bug report page is devoted to the issue. It says MySQL AB will not be speeding up information_schema query performance any time soon.

In theUsual we worked around this problem with a PHP function that queries information_schema if that is required, but by default parses the results of iterative SHOW TABLES commands. The SHOW TABLES method logic is simple, and will port readily to another application language. It runs 10-50 times faster than the equivalent information_schema query.

The following version looks for child tables in one database; it slows down a bit when modified to search all server DBs, but even then it is much faster than its information_schema equivalent. It assumes an available connection object $conn:

function childtables( $db, $table, $via_infoschema=FALSE ) {
  GLOBAL $conn;
  $ret = array();
  if( $via_infoschema ) {
    $res = mysqli_query( childtablesqry( $conn, $db, $table )) || die( mysqli_error($conn) );
    if( !is_bool( $res )) 
      while( $row = mysqli_fetch_row( $res )) 
        $ret[] = $row;
  }
  else {
    $tables = array();
    $res = mysqli_query( $conn, "SHOW TABLES" );
    while( $row = mysqli_fetch_row( $res )) $tables[] = $row[0];
    $res = mysqli_query( $conn "SELECT LOCATE('ANSI_QUOTES', @@sql_mode)" );
    $ansi_quotes = $res ? mysql_result( $res, 0 ) : 0;
    $q = $ansi_quotes ? '"' : "`";
    $sref = ' REFERENCES ' . $q . $table . $q . ' (' . $q;
    foreach( $tables as $referringtbl ) {
      $res = mysqli_query( $conn, "SHOW CREATE TABLE $referringtbl" );
      $row = mysqli_fetch_row( $res );
      if(( $startref = stripos( $row[1], $sref )) > 0 ) {
        $endref = strpos( $row[1], $q, $startref + strlen( $sref ));
        $referencedcol = substr( $row[1], $startref+strlen($sref), 
                                 $endref-$startref-strlen($sref) );
        $endkey = $startref; 
        while( substr( $row[1], $endkey, 1 ) <> $q ) $endkey--;
        $startkey = --$endkey;
        while( substr( $row[1], $startkey, 1 ) <> $q ) $startkey--;
        $referencingcol = substr( $row[1], $startkey+1, $endkey - $startkey );
        $ret[] = array( $db, $referringtbl, $referencingcol, $referencedcol );
      }
    }
  }
  return $ret;
}

function childtablesqry( $db, $table ) {
  return "SELECT c.table_schema,u.table_name,u.column_name,u.referenced_column_name " .
         "FROM information_schema.table_constraints AS c " .
         "INNER JOIN information_schema.key_column_usage AS u " .
         "USING( constraint_schema, constraint_name ) " .
         "WHERE c.constraint_type = 'FOREIGN KEY' " .
         "AND u.referenced_table_schema='$db' " .
         "AND u.referenced_table_name = '$table' " .
         "ORDER BY c.table_schema,u.table_name";
}


Return to the Artful Common Queries page