在 ALTER TABLE 前检查列是否存在 -- MySQL

22

在运行ALTER TABLE ADD column_name语句之前(或同时),是否有一种方法可以检查mySQL数据库中是否存在列? 类似于IF column DOES NOT EXIST ALTER TABLE

我尝试过ALTER IGNORE TABLE my_table ADD my_column,但如果我要添加的列已经存在,则仍会引发错误。

编辑:用例是升级已安装的Web应用程序中的表--因此为了保持简单,我希望确保我需要的列存在,如果它们不存在,则使用ALTER TABLE添加它们。


你有尝试查看alter table语句的语法描述吗?这些都是有文档记录的,你知道吗? - GolezTrol
1
你不觉得在不了解表结构的情况下运行DDL更改有些令人担忧吗?这是什么应用场景呢? - Russell
2
@GolezTrol-- 是的,显然我确实阅读了文档,但即使在使用您提供的IGNORE命令时,我仍然遇到错误--因此我想知道如何仅在列不存在时发出alter table命令。这是否不清楚? - julio
1
@Russell 一个使用案例是回滚脚本。如果正向脚本由于任何原因失败,回滚脚本可能会尝试回滚从未执行的更改。因此,该检查允许回滚脚本决定是否对任何给定的还原进行操作。 - user41871
2
@julio,标记一个答案为已接受并将一些欢迎的 SO 积分传递给试图帮助你的人永远不会太晚。 - Anne Gunn
10个回答

10

由于mysql控制语句(例如"IF")只在存储过程中起作用,因此可以创建临时存储过程并执行:

DROP PROCEDURE IF EXISTS add_version_to_actor;

DELIMITER $$

CREATE DEFINER=CURRENT_USER PROCEDURE add_version_to_actor ( ) 
BEGIN
DECLARE colName TEXT;
SELECT column_name INTO colName
FROM information_schema.columns 
WHERE table_schema = 'connjur'
    AND table_name = 'actor'
AND column_name = 'version';

IF colName is null THEN 
    ALTER TABLE  actor ADD  version TINYINT NOT NULL DEFAULT  '1' COMMENT  'code version of actor when stored';
END IF; 
END$$

DELIMITER ;

CALL add_version_to_actor;

DROP PROCEDURE add_version_to_actor;

9

实用函数和过程

首先,我有一系列实用函数和过程,用于删除外键、普通键和列等操作。我会将它们留在数据库中,以便需要时使用。

以下是这些函数和过程:

delimiter $$

create function column_exists(ptable text, pcolumn text)
  returns bool
  reads sql data
begin
  declare result bool;
  select
    count(*)
  into
    result
  from
    information_schema.columns
  where
    `table_schema` = 'my_database' and
    `table_name` = ptable and
    `column_name` = pcolumn;
  return result;
end $$

create function constraint_exists(ptable text, pconstraint text)
  returns bool
  reads sql data
begin
  declare result bool;
  select
    count(*)
  into
    result
  from
    information_schema.table_constraints
  where
    `constraint_schema` = 'my_database' and
    `table_schema` = 'my_database' and
    `table_name` = ptable and
    `constraint_name` = pconstraint;
  return result;
end $$

create procedure drop_fk_if_exists(ptable text, pconstraint text)
begin
  if constraint_exists(ptable, pconstraint) then
    set @stat = concat('alter table ', ptable, ' drop foreign key ', pconstraint);
    prepare pstat from @stat;
    execute pstat;
  end if;
end $$

create procedure drop_key_if_exists(ptable text, pconstraint text)
begin
  if constraint_exists(ptable, pconstraint) then
    set @stat = concat('alter table ', ptable, ' drop key ', pconstraint);
    prepare pstat from @stat;
    execute pstat;
  end if;
end $$

create procedure drop_column_if_exists(ptable text, pcolumn text)
begin
  if column_exists(ptable, pcolumn) then
    set @stat = concat('alter table ', ptable, ' drop column ', pcolumn);
    prepare pstat from @stat;
    execute pstat;
  end if;
end $$

delimiter ;

使用上述工具删除约束和列

有了这些工具,很容易使用它们来检查列和约束是否存在:

-- Drop service.component_id
call drop_fk_if_exists('service', 'fk_service_1');
call drop_key_if_exists('service', 'component_id');
call drop_column_if_exists('service', 'component_id');

-- Drop commit.component_id
call drop_fk_if_exists('commit', 'commit_ibfk_1');
call drop_key_if_exists('commit', 'commit_idx1');
call drop_column_if_exists('commit', 'component_id');

-- Drop component.application_id
call drop_fk_if_exists('component', 'fk_component_1');
call drop_key_if_exists('component', 'application_id');
call drop_column_if_exists('component', 'application_id');

在MySQL上工作。有史以来最好的答案!如果您运行一个模式并且在调整此答案以满足您的需求时不断出现错误,您可能需要使用DROP语句。在测试完成后,您可以删除它们。示例(前两行,然后是答案的代码): IF EXISTS column_exists,则删除函数。 IF EXISTS rename_column_if_exists,则删除过程。 - Juliano Suman Curti

8
你认为你能尝试这个吗?
SELECT IFNULL(column_name, '') INTO @colName
FROM information_schema.columns 
WHERE table_name = 'my_table'
AND column_name = 'my_column';

IF @colName = '' THEN 
    -- ALTER COMMAND GOES HERE --
END IF;

虽然这不是一个简单的问题,但你能看一下它是否适用于你吗?至少在等待更好的解决方案时可以这样做。


如果该列不存在,我会收到“字段列表中的未知列'column_name'”错误。 - bafromca
1
您的SQL语法存在错误,请检查与您的MySQL服务器版本相对应的手册,以获取正确的语法。位于第1行附近的语法为“IF @colName ='' THEN ALTER TABLE vegetables ADD vegetable_color VARCHAR(25”。 - AquaAlex

5

通过John Watson的例子,构造一个计数句子。

 SELECT count(*) FROM information_schema.COLUMNS
     WHERE COLUMN_NAME = '...'
     and TABLE_NAME = '...'
     and TABLE_SCHEMA = '...'

将结果保存为整数,然后把它作为条件应用于ADD COLUMN语句。


2
你能详细说明一下如何保存结果并将其作为添加列的条件吗? - mjk

1

虽然这是一个相当旧的帖子,但我仍然很高兴分享我的解决方案。如果列不存在,则肯定会发生异常,然后我会在表中创建该列。

我只是使用了以下代码:

 try
   {
         DATABASE_QUERY="SELECT gender from USER;";
         db.rawQuery(DATABASE_QUERY, null);
   }
   catch (Exception e)
   {
    e.printStackTrace();

        DATABASE_UPGRADE="alter table USER ADD COLUMN gender VARCHAR(10) DEFAULT 0;";
                db.execSQL(DATABASE_UPGRADE);
   } 

如果 USER 中有大量的性别条目,限制搜索到一个条目可能会更快,即 SELECT gender from user limit 1;。 - Keir

1

您可以使用以下代码测试列是否存在:

IF EXISTS (
     SELECT * FROM information_schema.COLUMNS
     WHERE COLUMN_NAME = '...'
     and TABLE_NAME = '...'
     and TABLE_SCHEMA = '...')

...

只需填写您的列名、表名和数据库名。


4
这让我遇到了一个 #1064 错误 - 你的 SQL 语法有误;请查阅与你的 MySQL 服务器版本相应的手册,以获取正确的语法。错误发生在以下语句附近:'IF EXISTS ( SELECT * FROM information_schema.COLUMNS WHERE COLUMN_NAME'。 - julio
2
IF语句只能在过程、触发器等内部使用。 - Ryan Williams

0

0
DELIMITER $$

DROP PROCEDURE IF EXISTS `addcol` $$
CREATE DEFINER=`admin`@`localhost` PROCEDURE `addcol`(tbn varchar(45), cn varchar(45), ct varchar(45))
BEGIN
#tbn: table name, cn: column name, ct: column type
DECLARE CONTINUE HANDLER FOR 1060 BEGIN END;
set cn = REPLACE(cn, ' ','_');
set @a = '';
set @a = CONCAT("ALTER TABLE `", tbn ,"` ADD column `", cn ,"` ", ct);
PREPARE stmt FROM @a;
EXECUTE stmt;

END $$

DELIMITER ;

0

您可以创建一个带有CONTINUE处理程序的过程,以防列存在(请注意,此代码在PHPMyAdmin中不起作用):

DROP PROCEDURE IF EXISTS foo;
CREATE PROCEDURE foo() BEGIN
    DECLARE CONTINUE HANDLER FOR 1060 BEGIN END;
    ALTER TABLE `tableName` ADD `columnName` int(10) NULL AFTER `otherColumn`;
END;
CALL foo();
DROP PROCEDURE foo;

如果该列已经存在,此代码不应引发任何错误。它将什么也不做,并继续执行其余的SQL。


-3
根据MYSQL社区:
IGNORE是MySQL对标准SQL的扩展。它控制ALTER TABLE在新表中存在唯一键重复或启用严格模式时发出警告的情况下如何工作。如果未指定IGNORE,则在出现重复键错误时,复制将被中止并回滚。如果指定了IGNORE,则仅使用具有唯一键重复的行中的一行。其他冲突行将被删除。不正确的值将被截断为最接近的匹配可接受值。
因此,一个有效的代码是: ALTER IGNORE TABLE CLIENTS ADD CLIENT_NOTES TEXT DEFAULT NULL; 此处发布的数据: http://dev.mysql.com/doc/refman/5.1/en/alter-table.html

抱歉,IGNORE不能做这件事。它只检查唯一键中的重复项,没有其他功能。 - bksi
嗨bksi,我希望在你添加建议之前,你已经查看了MySQL开发者社区关于这篇文章的内容,因为我已经给出了链接。如果你是正确的,而MySQL社区是错误的,请建议他们交叉检查他们的论坛并向你寻求帮助。 - JackSparrow
你测试过你的解决方案吗?因为我测试了它,它不起作用。当你有一个列antd并尝试使用ignore进行更改以添加相同的列时,会出现相同的错误。 - bksi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接