【问题标题】:Using ALTER to drop a column if it exists in MySQL使用 ALTER 删除 MySQL 中存在的列
【发布时间】:2010-09-15 11:40:59
【问题描述】:

如果存在 MySQL 表中的列,如何使用 ALTER 删除该列?

我知道我可以使用ALTER TABLE my_table DROP COLUMN my_column,但如果my_column 不存在,则会引发错误。有条件删除列的替代语法吗?

我使用的是 MySQL 4.0.18 版。

【问题讨论】:

  • Meta提到了这个问题。

标签: mysql ddl mysql4


【解决方案1】:

您可以使用此脚本,使用您的列、架构和表名

 IF EXISTS (SELECT *
                         FROM INFORMATION_SCHEMA.COLUMNS
                         WHERE TABLE_NAME = 'TableName' AND COLUMN_NAME = 'ColumnName' 
                                             AND TABLE_SCHEMA = SchemaName)
    BEGIN
       ALTER TABLE TableName DROP COLUMN ColumnName;
    END;
【解决方案2】:

对于 MySQL,没有: MySQL Feature Request

无论如何,允许这样做可以说是一个非常糟糕的主意:IF EXISTS 表示您正在对具有(对您而言)未知结构的数据库运行破坏性操作。在某些情况下,这对于快速而肮脏的本地工作来说是可以接受的,但如果您想针对生产数据运行这样的语句(在迁移等中),那么您就是在玩火。

但如果你坚持,首先在客户端简单地检查是否存在,或者捕获错误并不难。

MariaDB 从 10.0.2 开始还支持以下内容:

DROP [COLUMN] [IF EXISTS] col_name 

我。 e.

ALTER TABLE my_table DROP IF EXISTS my_column;

但可以说,依赖仅由 MySQL 的几个分支之一支持的非标准特性是一个坏主意。

【讨论】:

  • 有没有办法在纯 SQL 中做到这一点?
  • 哇。在 2005 年 - 9 年前提到过。我猜这是优先级列表...
  • MariaDB 从 10.0.2 开始支持
  • “允许这样做可以说是一个非常糟糕的主意,”——我不同意。为什么有人要对用户用例做出假设?我有一堆数据库,我需要让它们同步。当软件想要比人类更聪明时,这真的很烦人......
  • 14 年过去了,仍然没有。我认为这永远不会完成。
【解决方案3】:

也许解决这个问题的最简单方法(可行)是:

  • CREATE new_table AS SELECT id, col1, col2, ...(仅在最终表中您真正想要的列) FROM my_table;

  • 将 my_table 重命名为 old_table,将 new_table 重命名为 my_table;

  • 删除旧表;

如果需要,也可以保留 old_table 以备回滚。

这会起作用,但不会移动外键。您必须稍后将它们重新添加到 my_table ;还必须修复引用 my_table 的其他表中的外键(指向新的 my_table)。

祝你好运……

【讨论】:

    【解决方案4】:

    我刚刚构建了一个可重用的过程,可以帮助使DROP COLUMN 幂等:

    -- column_exists:
    
    DROP FUNCTION IF EXISTS column_exists;
    
    DELIMITER $$
    CREATE FUNCTION column_exists(
      tname VARCHAR(64),
      cname VARCHAR(64)
    )
      RETURNS BOOLEAN
      READS SQL DATA
      BEGIN
        RETURN 0 < (SELECT COUNT(*)
                    FROM `INFORMATION_SCHEMA`.`COLUMNS`
                    WHERE `TABLE_SCHEMA` = SCHEMA()
                          AND `TABLE_NAME` = tname
                          AND `COLUMN_NAME` = cname);
      END $$
    DELIMITER ;
    
    -- drop_column_if_exists:
    
    DROP PROCEDURE IF EXISTS drop_column_if_exists;
    
    DELIMITER $$
    CREATE PROCEDURE drop_column_if_exists(
      tname VARCHAR(64),
      cname VARCHAR(64)
    )
      BEGIN
        IF column_exists(tname, cname)
        THEN
          SET @drop_column_if_exists = CONCAT('ALTER TABLE `', tname, '` DROP COLUMN `', cname, '`');
          PREPARE drop_query FROM @drop_column_if_exists;
          EXECUTE drop_query;
        END IF;
      END $$
    DELIMITER ;
    

    用法:

    CALL drop_column_if_exists('my_table', 'my_column');
    

    例子:

    SELECT column_exists('my_table', 'my_column');       -- 1
    CALL drop_column_if_exists('my_table', 'my_column'); -- success
    SELECT column_exists('my_table', 'my_column');       -- 0
    CALL drop_column_if_exists('my_table', 'my_column'); -- success
    SELECT column_exists('my_table', 'my_column');       -- 0
    

    【讨论】:

      【解决方案5】:

      我知道这是一个旧线程,但有一种简单的方法可以在不使用存储过程的情况下处理此要求。这可能对某人有所帮助。

      set @exist_Check := (
          select count(*) from information_schema.columns 
          where TABLE_NAME='YOUR_TABLE' 
          and COLUMN_NAME='YOUR_COLUMN' 
          and TABLE_SCHEMA=database()
      ) ;
      set @sqlstmt := if(@exist_Check>0,'alter table YOUR_TABLE drop column YOUR_COLUMN', 'select ''''') ;
      prepare stmt from @sqlstmt ;
      execute stmt ;
      

      希望这对某人有所帮助,就像对我一样(经过大量试验和错误)。

      【讨论】:

      • 确实如此。谢谢@Pradeep
      【解决方案6】:

      在 MySQL 中对此没有语言级别的支持。这是 5.0+ 中涉及 MySQL information_schema 元数据的解决方法,但它不会解决您在 4.0.18 中的问题。

      drop procedure if exists schema_change;
      
      delimiter ';;'
      create procedure schema_change() begin
      
          /* delete columns if they exist */
          if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column1') then
              alter table table1 drop column `column1`;
          end if;
          if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column2') then
              alter table table1 drop column `column2`;
          end if;
      
          /* add columns */
          alter table table1 add column `column1` varchar(255) NULL;
          alter table table1 add column `column2` varchar(255) NULL;
      
      end;;
      
      delimiter ';'
      call schema_change();
      
      drop procedure if exists schema_change;
      

      我在blog post中写了一些更详细的信息。

      【讨论】:

      • 我认为将 DrHyde 的贡献总结为评论很重要,因为它在自己的答案中并不明显。确保检查您没有修改其他数据库: SELECT * from information_schema.columns WHERE table_name = "country" AND column_name = "updated_at" AND table_schema = DATABASE() \G
      • 如果您不想从“drop procedure if exists schema_change;”中得到警告添加“设置 sql_notes = 0;”在第一行之前添加“set sql_notes = 1;”在最后一行之后。详情->stackoverflow.com/questions/27616564/suppress-mysql-warnings
      • "delimiter" 应该没有 '' (例如 --> delimiter ;;)
      【解决方案7】:

      我意识到这个线程现在已经很老了,但我遇到了同样的问题。 这是我使用 MySQL Workbench 的非常基本的解决方案,但效果很好......

      1. 获取新的 sql 编辑器并执行 SHOW TABLES 以获取表列表
      2. 选择所有行,然后从上下文菜单中选择复制到剪贴板(未加引号)
      3. 将名称列表粘贴到另一个编辑器选项卡中
      4. 写下你的查询,即ALTER TABLE x DROP a;
      5. 进行一些复制和粘贴操作,最终得到每个表的单独查询
      6. 发生错误时切换工作台是否应停止
      7. 点击执行并查看输出日志

      任何有桌子的桌子现在都没有 任何未在日志中显示错误的表

      然后您可以找到/替换 'drop a' 将其更改为 'ADD COLUMN b INT NULL' 等,然后再次运行整个过程......

      有点笨拙,但最后你得到了最终结果,你可以控制/监控整个过程,记得保存你的 sql 脚本以防你再次需要它们。

      【讨论】:

        【解决方案8】:

        Chase Seibert 的回答有效,但我想补充一点,如果您有多个模式,您想因此更改 SELECT:

        select * from information_schema.columns where table_schema in (select schema()) and table_name=...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-06
          • 1970-01-01
          • 1970-01-01
          • 2018-12-13
          • 2013-04-06
          • 2012-01-03
          相关资源
          最近更新 更多