Mysql:删除一个唯一的密钥,如果存在 - 但不知道密钥的名称或它是否存在

Mysql:删除一个唯一的密钥,如果存在 - 但不知道密钥的名称或它是否存在

问题描述:

不要问为什么(因为答案是'我们完全奇怪和bepsoke设置...'),但我需要在不知道密钥名称的情况下删除表中的唯一密钥(如果存在) - 仅列出组成密钥的列。Mysql:删除一个唯一的密钥,如果存在 - 但不知道密钥的名称或它是否存在

例如我有这个表

CREATE TABLE `my_table` (
          `id` binary(36) NOT NULL, 
          `username` char(12) NOT NULL DEFAULT '', 
          `password` char(32) NOT NULL DEFAULT '', 
          `role` char(1) NOT NULL, 
          PRIMARY KEY (`id`), 
          UNIQUE KEY `username_2` (`username`,`role`), 
          UNIQUE KEY `username` (`username`), 
          UNIQUE KEY `username_3` (`username`), 
          UNIQUE KEY `username_4` (`username`), 
          ) 

,我想放弃了(username,role)关键,但不是别人(虽然只有当它的存在! - 的时候,我得到了它,它可能已经被删除)

现在我知道如何删除一个关键的时候,我知道了约束名

ALTER TABLE `my_table` DROP KEY `name_of_my_key` 

,我知道如何检查一个唯一的密钥存在

SELECT EXISTS (SELECT constraint_name 
       FROM INFORMATION_SCHEMA.table_constraints 
       WHERE table_name = 'my_table' AND constraint_type='UNIQUE'); 

但是,当我只知道构成唯一密钥的列时,这仍然无法帮助我。

感谢您的帮助......当我知道一个键存在,我怎么能仅知道哪些列推导constraint_name命令(S)组成的约束,让我放弃呢?注意约束名称,因为mySQL允许您重复添加相同的密钥,如上面3个相同的(username)密钥。

我发现的唯一的东西是文章,如Drop Foreign Key without knowing the name of the constraint? 它似乎只显示如何获取约束名称,不能以编程方式标识要删除的约束。

最后,如果有人知道为什么地狱的MySQL将允许你反复添加相同的键(非主),以表我会洗耳恭听。 MySQL,你太疯狂了。

+0

也许,'DISTINCT'关键字会有帮助吗?无论哪种方式,这是一种奇怪的想法后面的数据库结构的逻辑,我想出了一个直接的答案。 (就像为什么要动态地改变表格一样) – Xorifelse

+0

我确实说过不要问为什么;)但是既然你问过......如果你(或者以前的某个人给你留下了一个作业)使用相同的表格设置数据库,将副本分发到1000个不同的地方,然后为它们部署各种补丁。所以我们知道有一些有限制,有些则没有,并且我们无法确定限制的命名。难道你不喜欢成为一名开发者... –

在@ shadow的关于key_column_usage和序号位置的提示的帮助下,我现在创建了这个漂亮的存储过程,它接受一个表,键定义(逗号分隔的列列表)和数据库。然后它检查该键定义是否存在唯一键并生成SQL以删除它们。

我只生产了SQL这里,以避免人们越来越运行此过程的破坏性操作,只是删除初始从@sqlstmt“选择”,如果你真的想要它做的业务。

也可以修改这个在密钥类型采取作为一个参数,而不是仅仅依靠硬编码UNIQUE

CREATE PROCEDURE sp_drop_unique_key_if_exists 
(
given_table VARCHAR(64), 
given_key  TEXT, -- In comma sep form '(col1, col2, ...)' note the brackets are important! 
db    VARCHAR(64) 
) 
BEGIN 

DECLARE drop_constraints TEXT; 
DECLARE dbase    VARCHAR(64); 

SET dbase = IFNULL(db, 'my_db'); 

SELECT  group_concat('DROP KEY `',unique_constraints, '`') 
      INTO drop_constraints 
    FROM    
    (SELECT  IF(REPLACE(given_key, ' ', '') 
         = CONCAT('(', GROUP_CONCAT(kcu.column_name ORDER BY ordinal_position), ')'), 
         tc.constraint_name, null) 
        AS unique_constraints 
     FROM  INFORMATION_SCHEMA.table_constraints tc 
     INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu 
        ON kcu.constraint_name = tc.constraint_name 
        AND kcu.constraint_schema = tc.constraint_schema 
     WHERE  tc.table_name = given_table AND tc.constraint_schema = dbase 
        AND tc.constraint_type='UNIQUE' 
     GROUP BY tc.constraint_name 
    ); 

-- drop the key or keys 
IF drop_constraints IS NOT NULL THEN 
    SET @sqlstmt = CONCAT('SELECT \"ALTER TABLE ',dbase,'.', given_table, ' ',drop_constraints,'\"'); 
    PREPARE st FROM @sqlstmt; 
    EXECUTE st; 
    DEALLOCATE PREPARE st; 
ELSE 
    SELECT CONCAT('Cannot find key ', given_key, ' on ', given_table) DropUniqueKeyErrorMessage; 
END IF; 

END $$ 

INFORMATION_SCHEMA.KEY_COLUMN_USAGE表保存其中的字段组成的索引的信息。

可以返回,涉及到给定表与下面的查询指定的字段指数(或指数)的名称。存在子查询确保索引具有两个字段,而不存在则确保索引没有任何其他字段。进一步的限制,例如顺序位置也可以包含在查询中。

select CONSTRAINT_NAME 
from INFORMATION_SCHEMA.KEY_COLUMN_USAGE t1 
where TABLE_NAME='my_table' 
    and CONSTRAINT_SCHEMA='myshema' 
    and COLUMN_NAME='username' 
    and exists (select 1 
       from INFORMATION_SCHEMA.KEY_COLUMN_USAGE t2 
       where TABLE_NAME='my_table' 
        and CONSTRAINT_SCHEMA='myshema' 
        and COLUMN_NAME='role' 
        and t2.CONSTRAINT_NAME=t1.CONSTRAINT_NAME 
        and t2.CONSTRAINT_SCHEMA=t1.CONSTRAINT_SCHEMA) 
    and not exists (select 1 
        from INFORMATION_SCHEMA.KEY_COLUMN_USAGE t2 
        where TABLE_NAME='my_table' 
         and CONSTRAINT_SCHEMA='myshema' 
         and COLUMN_NAME NOT IN ('username','role') 
         and t2.CONSTRAINT_NAME=t1.CONSTRAINT_NAME 
         and t2.CONSTRAINT_SCHEMA=t1.CONSTRAINT_SCHEMA) 

但是,上述查询只返回要删除的索引的名称(如果有的话)。您需要动态连接sql命令以使用prepared statement删除索引。

+0

真棒感谢key_column_usage是一个很好的发现,你的子查询很好地解释。其中一个不涉及的事情是索引中的列的顺序(即如果存在的话,它也会找到'(role,username)'键),但是你给了我一个前进方向。干杯。 –

+0

正如我在答案中所写的那样:“进一步的限制,比如顺序位置也可以包含在查询中。”如果您点击文档链接,您会发现ordinal_position列定义了索引中字段的序号位置。只需将它添加到你的where子句。 – Shadow