执行 sql:
SELECTconstraint_name,table_name,column_name,referenced_table_name,referenced_column_name
FROMinformation_schema.key_column_usage
WHEREtable_schema= '${databaseName}'
ANDreferenced_table_name IS NOT NULL
将执行结果复制到临时文件中:
blob_triggers_ibfk_1 blob_triggers SCHED_NAME triggers SCHED_NAME
blob_triggers_ibfk_1 blob_triggers TRIGGER_NAME triggers TRIGGER_NAME
blob_triggers_ibfk_1 blob_triggers TRIGGER_GROUP triggers TRIGGER_GROUP
cron_triggers_ibfk_1 cron_triggers SCHED_NAME triggers SCHED_NAME
cron_triggers_ibfk_1 cron_triggers TRIGGER_NAME triggers TRIGGER_NAME
cron_triggers_ibfk_1 cron_triggers TRIGGER_GROUP triggers TRIGGER_GROUP
simple_triggers_ibfk_1 simple_triggers SCHED_NAME triggers SCHED_NAME
simple_triggers_ibfk_1 simple_triggers TRIGGER_NAME triggers TRIGGER_NAME
simple_triggers_ibfk_1 simple_triggers TRIGGER_GROUP triggers TRIGGER_GROUP
simprop_triggers_ibfk_1 simprop_triggers SCHED_NAME triggers SCHED_NAME
simprop_triggers_ibfk_1 simprop_triggers TRIGGER_NAME triggers TRIGGER_NAME
simprop_triggers_ibfk_1 simprop_triggers TRIGGER_GROUP triggers TRIGGER_GROUP
triggers_ibfk_1 triggers SCHED_NAME job_details SCHED_NAME
triggers_ibfk_1 triggers JOB_NAME job_details JOB_NAME
triggers_ibfk_1 triggers JOB_GROUP job_details JOB_GROUP
稍微处理下输出如下:
表 blob_triggers 外键引用了表 triggers
表 cron_triggers 外键引用了表 triggers
表 triggers 外键引用了表 job_details
表 simple_triggers 外键引用了表 triggers
表 simprop_triggers 外键引用了表 triggers
这种结构还是不够直观,没法一下子看出哪些表未被任何其他表外键引用( 只有删除或清空未被任何其他表外键引用的表的数据时,才不会提示“删除失败,该表已被表xxx 外键引用” ),那么该如何展示才够直观呢,才能一下子看出来哪些表未被其他任何表引用呢?我们想到了树结构,即父id自关联的树结构,比如公司的部门树结构,删除叶子节点不会提示被引用,而删除父节点会提示被引用,无法删除,所以我们应该把被外键引用的表作为树的父节点,把未被任何其他表外键引用的表作为叶子节点。
完整代码:
FKReferenceTopologyPrintTest.java:
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.*;public class FKReferenceTopologyPrintTest {public static void main(String[] args) {String filePath = "\\xxx\\...\\xxx_FKReferenceList.txt";List<RelationDirectionVO> relations = readFile2Relations( filePath );Map<String, TableNode> map_tableName_tableNode = new HashMap<>();for( RelationDirectionVO relation:relations ){// a 引用了 b 就表示 a 是 b 的孩子System.out.println( "表 [" + relation.getTableName_from() + "]" + " 外键引用了表 [" + relation.getTableName_to() + "]" );String tableNameTo = relation.getTableName_to();String tableNameFrom = relation.getTableName_from();TableNode tableNode_parent = map_tableName_tableNode.get(tableNameTo);if( tableNode_parent == null ){tableNode_parent = new TableNode();tableNode_parent.setTableName( tableNameTo );map_tableName_tableNode.put( tableNameTo,tableNode_parent );}TableNode tableNode_child= map_tableName_tableNode.get(tableNameFrom);if( tableNode_child == null ){tableNode_child = new TableNode();tableNode_child.setTableName( tableNameFrom );map_tableName_tableNode.put( tableNameFrom,tableNode_child );}// 设置 children 关系List<TableNode> children = tableNode_parent.getChildren();if( children == null ){children = new ArrayList<>();tableNode_parent.setChildren( children );}children.add( tableNode_child );// 设置 parent 关系List<TableNode> parents = tableNode_child.getParents();if( parents == null ){parents = new ArrayList<>();tableNode_child.setParents( parents );}parents.add( tableNode_parent );}System.out.println();System.out.println();// printSet<String> tableNames = map_tableName_tableNode.keySet();for( String tableName:tableNames ){TableNode tableNode = map_tableName_tableNode.get(tableName);List<TableNode> parents = tableNode.getParents();if( parents != null && parents.size() > 0 ){// 该节点 有 parent 节点,则不直接输出此节点了( 此节点必然会在输出其父节点时被打印出来 )continue;}printTableNode( "",tableNode );System.out.println();}}private static List<RelationDirectionVO> readFile2Relations(String filePath) {BufferedReader reader = null;try {reader = new BufferedReader(new FileReader(filePath));String line = reader.readLine();Map<String, TableNode> map_tableName_tableNode = new HashMap<>();Map<String, RelationDirectionVO> map_key_relation = new HashMap<>();while (line != null) {// System.out.println(line);String[] array = line.split("\\s+");String table_name = array[1];String referenced_table_name = array[3];String key = table_name + "--->" + referenced_table_name;if( map_key_relation.containsKey( key ) ){line = reader.readLine();continue;}RelationDirectionVO relation = new RelationDirectionVO();relation.setTableName_from( table_name );relation.setTableName_to( referenced_table_name );map_key_relation.put( key,relation );}List<RelationDirectionVO> relationList = new ArrayList<>();Set<String> keys = map_key_relation.keySet();for( String key:keys ){RelationDirectionVO relation = map_key_relation.get(key);relationList.add( relation );}return relationList;} catch (Exception e) {e.printStackTrace();return null;} finally {if (reader != null) {try {reader.close();} catch (Exception e) {e.printStackTrace();}}}}private static void printTableNode( String padding,TableNode tableNode) {if( tableNode == null ){return;}System.out.println( padding + "[" + tableNode.getTableName() + "]" );List<TableNode> children = tableNode.getChildren();if( children == null || children.size() == 0 ){return;}for( TableNode child:children ){printTableNode( padding + "\t",child );}return;}
}
RelationDirectionVO.java:
import lombok.Getter;
import lombok.Setter;import java.io.Serializable;@Getter
@Setter
public class RelationDirectionVO implements Serializable {private String tableName_from;private String tableName_to;}
TableNode.java:
import lombok.Getter;
import lombok.Setter;import java.util.List;@Getter
@Setter
public class TableNode {private String tableName;private List<TableNode> children;private List<TableNode> parents;
}
输出外检引用拓扑结构关系:
表 blob_triggers 外键引用了表 triggers
表 cron_triggers 外键引用了表 triggers
表 simple_triggers 外键引用了表 triggers
表 simprop_triggers 外键引用了表 triggers
表 triggers 外键引用了表 job_detailsjob_detailstriggersblob_triggerscron_triggerssimple_triggerssimprop_triggers
这样我们就知道叶子节点表示的是未被其他任何表外键引用的表了,可以直接删除或清理数据