【问题标题】:Drop unnamed Foreign Key in MySql在 MySql 中删除未命名的外键
【发布时间】:2012-12-13 09:38:09
【问题描述】:

如果创建的外键没有名字,MySql 会给它一个默认的。例如,对于表“Test”,外键将命名为“test_ibfk_1”。当我使用此名称在本地删除外键时,它就像一个魅力,但在开发服务器上失败并显示 errno: 152。

我知道这个名字是区分大小写的,但是无论是小写还是大写,结果都是一样的。

我的问题:依赖默认名称来操作约束是否安全(至少在 MySql 中)?

提前致谢!

【问题讨论】:

    标签: mysql foreign-keys


    【解决方案1】:

    您需要知道外键的名称。如果创建时没有名称,则会自动生成名称。您应该获得有关外键的信息。

    使用这些查询之一来获取外键名称 -

    SELECT
      constraint_name
    FROM
      information_schema.REFERENTIAL_CONSTRAINTS
    WHERE
      constraint_schema = <'db_name'> AND table_name = <'table_name'>;
    
    
    SELECT *
    FROM
      information_schema.KEY_COLUMN_USAGE
    WHERE
      constraint_schema = <'db_name'> AND table_name = <'table_name'> AND   
      referenced_table_name IS NOT NULL;
    

    ...并使用ALTER TABLE &lt;table_name&gt; DROP INDEX &lt;fk_name&gt;; 删除外键。

    【讨论】:

    • 上述查询是否适用于非管理员?总的来说很好知道信息!谢谢!
    • 很遗憾,为了获得这种查询的 fk_name 而不得不经历这种耐力跋涉。应该直截了当地说:ALTER table table_name DROP CONSTRAINT ON col_name(不管它的名字是什么)。毕竟,给定键上只能有一个这样的约束。每个人都对吗?
    • @IfediOkonkwo,我完全同意。如果您使用 Liquibase 之类的东西来管理跨多个环境的数据库的模式更改,则问题尤其严重,并且不能保证在运行它们的每个数据库上生成相同的 FK 约束名称。这意味着您不能在将来的更改中依赖该约束名称。尽管您显然可以从一开始就使用固定名称来命名约束。
    【解决方案2】:

    匆忙的读者,不要离开这个答案,只是因为它很长。你不会找到其他解决方案,我保证:)

    接受的答案不会删除密钥,它只会找到它的名称。要真正删除名称未知的密钥,您需要使用准备好的语句。最通用的解决方案是这个脚本,您可以使用五个变量进行自定义:

    -- YOU MUST SPECIFY THESE VARIABLES TO FULLY IDENTIFY A CONSTRAINT
    SET @table_name = '...';
    SET @column_name = '...';
    SET @referenced_table_name = '...';
    SET @referenced_column_name = '...';
    -- make sure to limit queries to a single db schema
    SET @db_name = '...'; 
    
    -- find the name of the foreign key and store it in a var
    SET @constraint_name = (
        SELECT constraint_name
        FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
        WHERE TABLE_NAME = @table_name
            AND COLUMN_NAME = @column_name
            AND CONSTRAINT_SCHEMA = @db_name
            AND referenced_table_name = @referenced_table_name
            AND referenced_column_name = @referenced_column_name);
    
    
    -- prepare the drop statement in a string and run it
    SET @s = concat('alter table ', @table_name, ' drop foreign key ', @constraint_name);
    
    PREPARE stmt FROM @s;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    

    【讨论】:

      【解决方案3】:

      您可以在不知道名称的情况下删除,创建一个连接查询并自动执行:

      set @s:='';
      select @s:=concat(@s, 'alter table ', 'your_table', ' drop foreign key ',CONSTRAINT_NAME, ';')
      from information_schema.key_column_usage
      where CONSTRAINT_SCHEMA = 'your_database'
        and TABLE_NAME ='your_table'
        and REFERENCED_TABLE_NAME = 'the_foreign_reference_table';
      prepare stmt from @s;
      execute stmt;
      
      deallocate prepare stmt;
      

      这样你就不需要知道外键的名字,只需要你要移除外键的table_name,数据库名和引用表。

      【讨论】:

        【解决方案4】:

        如果有人对如何从表中删除所有外键感兴趣,我们在更新存储过程中使用此代码(该示例应用于我们内部数据库中的表 db_Order):

        DECLARE counter INT DEFAULT 1;
        DECLARE foreignKeyDeleteStatement VARCHAR(200) DEFAULT '';
        
        ...
        
        SET counter = (SELECT COUNT(1) FROM information_schema.TABLE_CONSTRAINTS
            WHERE CONSTRAINT_TYPE = 'FOREIGN KEY' AND table_schema=DATABASE() AND table_name='db_Order');
            SELECT 'Number of foreignKeys on table db_Order = ' + counter;
        
        WHILE counter > 0 DO
                        SELECT concat('ALTER TABLE ', TABLE_NAME, ' DROP FOREIGN KEY ', CONSTRAINT_NAME, ';') 
                        FROM information_schema.key_column_usage 
                        WHERE CONSTRAINT_SCHEMA = DATABASE()
                        AND TABLE_NAME='db_order' 
                        AND REFERENCED_TABLE_NAME IS NOT NULL
                        LIMIT 1
                        INTO foreignKeyDeleteStatement;
                        
                        SELECT foreignKeyDeleteStatement;
                        
                        SET @s = foreignKeyDeleteStatement;
                
                        PREPARE stmt1 FROM @s;
                        EXECUTE stmt1;
                        DEALLOCATE PREPARE stmt1;
                        
                        SET counter = counter - 1;
        END WHILE;
        

        我希望这对某人有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-16
          • 2015-02-22
          • 2011-01-02
          • 2023-03-23
          • 2012-01-10
          • 1970-01-01
          • 2014-08-03
          相关资源
          最近更新 更多