我想获得按依赖顺序排序的表名。
例如:如果我有表
users
,users_orders
和orders
我想得到表名这个顺序:users
(或orders
无所谓),orders
,users_orders
。
如果有办法请帮忙
听起来您正在尝试转储具有外键定义的表,并且您想确保首先转储“父”表,以便可以确保引用它们的“子”表的外键定义有效。
一般情况下你不能这样做,因为循环依赖是可能的。
例如,如果您有
users
和 teams
,其中每个用户都有对他们所属团队的引用,但 teams
也有对作为团队队长的特定用户的引用 captain
,你知道吗?想先列出users
,还是先列出teams
?
另一种解决方案是以您想要的任何顺序输出所有表,但没有外键定义。在列出所有表格及其数据之后,然后使用
ALTER TABLE...ADD FOREIGN KEY
命令进行操作。
另一种替代解决方案——mysqldump 使用的解决方案——是在开始时以
SET FOREIGN_KEY_CHECKS=0
。然后你可以定义外键约束而不用担心引用表是否已经创建。表格按字母顺序转储。
但更直接地回答您的问题:您可以使用 INFORMATION_SCHEMA 来发现存在哪些表依赖关系。
SELECT table_schema, table_name,
GROUP_CONCAT(column_name ORDER BY ordinal_position) AS `columns`,
MAX(referenced_table_schema) AS referenced_table_schema,
MAX(referenced_table_name) AS referenced_table_name,
GROUP_CONCAT(referenced_column_name ORDER BY ordinal_position) AS `ref_columns`
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE referenced_table_schema IS NOT NULL
GROUP BY table_schema, table_name;
MySQL 直到 MySQL 8.0(仍在开发中)才支持递归查询。因此,您可能必须将依赖项数据提取到您的应用程序中,并确定要转储它们的顺序。
但是你仍然无法以这种方式处理循环。您将不得不使用我上面描述的替代方案之一。
你可以做这样的事情。
获取所有没有外键依赖的表的列表。
SELECT TABLE_NAME
FROM information_schema.tables
WHERE table_schema='my_database'
AND TABLE_NAME NOT IN (
SELECT DISTINCT TABLE_NAME
FROM information_schema.key_column_usage WHERE table_schema='my_database'
AND referenced_table_name IS NOT NULL;
获取在另一个没有任何外键依赖项的表上具有外键依赖项的表的列表。
SELECT DISTINCT TABLE_NAME
FROM information_schema.referential_constraints
WHERE CONSTRAINT_SCHEMA='my_database'
AND referenced_table_name NOT IN
(SELECT DISTINCT TABLE_NAME
FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY')
AND TABLE_NAME NOT IN
(SELECT DISTINCT TABLE_NAME
FROM information_schema.referential_constraints
WHERE referenced_table_name IN
(SELECT DISTINCT TABLE_NAME
FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY'));
获取具有外键依赖关系的表列表,这些表本身具有外键依赖关系。
SELECT DISTINCT TABLE_NAME
FROM information_schema.referential_constraints
WHERE CONSTRAINT_SCHEMA='my_database'
AND referenced_table_name IN
(SELECT DISTINCT TABLE_NAME
FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY');
然后你可以在像下面这样的脚本中使用它们。它不会解决上述循环依赖问题,但会创建一个 MySQL 转储文件,其中表按外键依赖排序。
#!/usr/bin/env bash
#
# staged-mysqldump.sh
#
# Runs mysqldump against a database and splits the output into
# three directories.
#
# <database_name>-STAGE-1 contains dumps of all of the tables
# that don't have any foreign key constraints.
# <database_name>-STAGE-2 contains dumps of all of the tables
# that have only have foreign key constraints with other tables
# that don't have foreign key constraints.
# <database_name>-STAGE-3 contains dumps of the rest of the tables.
#
#
DATABASE="$1"
DUMPDIR="/var/tmp"
MYSQL_CREDENTIALS="~/.my.cnf"
DUMPDATE="$(date +%Y%m%d)"
#
# Write a statement that drops the database if it exists and
# then write a create database statement to simulate a regular
# mysqldump.
#
echo "DROP DATABASE IF EXISTS $DATABASE;" >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
mysql --defaults-extra-file=$MYSQL_CREDENTIALS --skip-column-names --batch --execute "SHOW CREATE DATABASE $DATABASE" | sed "s/^$DATABASE\s\+//;s/$/;/" >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
#
# Dump the stage 1 tables.
#
printf "Dumping tables for %s - Stage 1\n" "$DATABASE"
STAGE_1_TABLES=$(mysql --defaults-extra-file=$MYSQL_CREDENTIALS --skip-column-names --batch --execute "SELECT table_name FROM information_schema.tables WHERE table_schema='$DATABASE' AND table_name NOT IN (SELECT distinct table_name FROM information_schema.key_column_usage WHERE table_schema='$DATABASE' AND referenced_table_name IS NOT NULL)")
printf "Stage 1 Start Time: %(%Y-%m-%d - %H:%M:%S)T\n"
mysqldump --defaults-extra-file=$MYSQL_CREDENTIALS --databases $DATABASE --tables $STAGE_1_TABLES >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
printf "Finished dumping tables for %s - Stage 1\n" "$DATABASE"
printf "Stage 1 End Time: %(%Y-%m-%d - %H:%M:%S)T\n"
#
# Dump the stage 2 tables.
#
printf "Dumping tables for %s - Stage 2\n" "$DATABASE"
STAGE_2_TABLES=$(mysql --defaults-extra-file=$MYSQL_CREDENTIALS --skip-column-names --batch --execute "SELECT DISTINCT table_name FROM information_schema.referential_constraints WHERE constraint_schema='$DATABASE' AND referenced_table_name NOT IN (SELECT DISTINCT table_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY') AND table_name NOT IN (SELECT DISTINCT table_name FROM information_schema.referential_constraints WHERE referenced_table_name IN (SELECT DISTINCT table_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'))")
printf "Stage 2 Start Time: %(%Y-%m-%d - %H:%M:%S)T\n"
mysqldump --defaults-extra-file=$MYSQL_CREDENTIALS --databases $DATABASE --tables $STAGE_2_TABLES >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
printf "Finished dumping tables for %s - Stage 2\n" "$DATABASE"
printf "Stage 2 End Time: %(%Y-%m-%d - %H:%M:%S)T\n"
#
# Dump the stage 3 tables.
#
printf "Dumping tables for %s - Stage 3\n" "$DATABASE"
STAGE_3_TABLES=$(mysql --defaults-extra-file=$MYSQL_CREDENTIALS --skip-column-names --batch --execute "SELECT DISTINCT table_name FROM information_schema.referential_constraints WHERE constraint_schema='$DATABASE' AND referenced_table_name IN (SELECT DISTINCT table_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY')")
printf "Stage 3 Start Time: %(%Y-%m-%d - %H:%M:%S)T\n"
mysqldump --defaults-extra-file=$MYSQL_CREDENTIALS --databases $DATABASE --tables $STAGE_2_TABLES >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
printf "Finished dumping tables for %s - Stage 3\n" "$DATABASE"
printf "Stage 3 End Time: %(%Y-%m-%d - %H:%M:%S)T\n"
如果你使用它,你可能想要调整传递给 MySQL 的选项。
mysqldump --defaults-extra-file=$MYSQL_CREDENTIALS --数据库 $DATABASE --tables $STAGE_2_TABLES >> $DUMPDIR/$DATABASE-$DUMPDATE-dump.sql
这应该是 $STAGE_3_TABLES。 $STAGE_2_TABLES 插入两次。