MySQL table 名称按依赖顺序排序
MySQL table names ordered by dependency order
我想获得 table 个按依赖顺序排序的名称。
例如:如果我有 table users
、users_orders
和 orders
我想得到 table 这个顺序的名字:users
(或orders
无所谓),orders
,users_orders
。
如果有办法请帮忙
听起来您正在尝试转储具有外键定义的 tables,并且您想确保首先转储 "parent" tables,因此 "child" table 可以确保引用它们的外键定义有效。
一般不能这样做,因为循环依赖是可能的。
例如,如果您有 users
和 teams
,其中每个用户都引用了他们所属的团队,但 teams
也引用了 captain
对于作为队长的特定用户,您要先列出 users
,还是先列出 teams
?
另一种解决方案是以您想要的任何顺序输出所有 table,但没有外键定义。在列出所有 table 及其数据之后,然后使用 ALTER TABLE...ADD FOREIGN KEY
命令。
另一种替代解决方案(mysqldump 使用的解决方案)是在开头 SET FOREIGN_KEY_CHECKS=0
。然后你可以定义外键约束而不用担心引用的 table 是否已经创建。 table 按字母顺序转储。
但更直接地回答您的问题:您可以使用 INFORMATION_SCHEMA 来发现存在哪些 table 依赖项。
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(仍在开发中)之前不支持递归查询。因此,您可能必须将依赖项数据提取到您的应用程序中,并确定要转储它们的顺序。
但是您仍然无法以这种方式处理循环。您必须使用我上面描述的替代方法之一。
你可以这样做。
获取所有 table 没有外键依赖项的列表。
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;
获取 table 的列表,这些 table 对另一个没有外键依赖的 table 具有外键依赖性。
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'));
获取具有外键依赖性的 table 的列表,这些 table 本身具有外键依赖性。
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 转储文件,其中 table 按外键依赖排序。
#!/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=""
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 的选项。
我想获得 table 个按依赖顺序排序的名称。
例如:如果我有 table users
、users_orders
和 orders
我想得到 table 这个顺序的名字:users
(或orders
无所谓),orders
,users_orders
。
如果有办法请帮忙
听起来您正在尝试转储具有外键定义的 tables,并且您想确保首先转储 "parent" tables,因此 "child" table 可以确保引用它们的外键定义有效。
一般不能这样做,因为循环依赖是可能的。
例如,如果您有 users
和 teams
,其中每个用户都引用了他们所属的团队,但 teams
也引用了 captain
对于作为队长的特定用户,您要先列出 users
,还是先列出 teams
?
另一种解决方案是以您想要的任何顺序输出所有 table,但没有外键定义。在列出所有 table 及其数据之后,然后使用 ALTER TABLE...ADD FOREIGN KEY
命令。
另一种替代解决方案(mysqldump 使用的解决方案)是在开头 SET FOREIGN_KEY_CHECKS=0
。然后你可以定义外键约束而不用担心引用的 table 是否已经创建。 table 按字母顺序转储。
但更直接地回答您的问题:您可以使用 INFORMATION_SCHEMA 来发现存在哪些 table 依赖项。
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(仍在开发中)之前不支持递归查询。因此,您可能必须将依赖项数据提取到您的应用程序中,并确定要转储它们的顺序。
但是您仍然无法以这种方式处理循环。您必须使用我上面描述的替代方法之一。
你可以这样做。
获取所有 table 没有外键依赖项的列表。
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;
获取 table 的列表,这些 table 对另一个没有外键依赖的 table 具有外键依赖性。
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'));
获取具有外键依赖性的 table 的列表,这些 table 本身具有外键依赖性。
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 转储文件,其中 table 按外键依赖排序。
#!/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="" 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 的选项。