MySQL table 名称按依赖顺序排序

MySQL table names ordered by dependency order

我想获得 table 个按依赖顺序排序的名称。

例如:如果我有 table usersusers_ordersorders 我想得到 table 这个顺序的名字:users(或orders无所谓),ordersusers_orders

如果有办法请帮忙

听起来您正在尝试转储具有外键定义的 tables,并且您想确保首先转储 "parent" tables,因此 "child" table 可以确保引用它们的外键定义有效。

一般不能这样做,因为循环依赖是可能的。

例如,如果您有 usersteams,其中每个用户都引用了他们所属的团队,但 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(仍在开发中)之前不支持递归查询。因此,您可能必须将依赖项数据提取到您的应用程序中,并确定要转储它们的顺序。

但是您仍然无法以这种方式处理循环。您必须使用我上面描述的替代方法之一。

你可以这样做。

  1. 获取所有 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;
    
  2. 获取 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'));
    
  3. 获取具有外键依赖性的 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 的选项。