【问题标题】:add column to mysql table if it does not exist如果它不存在,则将列添加到 mysql 表
【发布时间】:2010-11-01 15:45:10
【问题描述】:

我的研究和实验还没有得出答案,所以我希望能得到一些帮助。

我正在修改以前版本中没有我现在要添加的列的应用程序的安装文件。我不想手动添加该列,而是在安装文件中并且仅当新列不存在于表中时。

创建表如下:

CREATE TABLE IF NOT EXISTS `#__comm_subscribers` (
      `subscriber_id` int(11) NOT NULL auto_increment,
      `user_id` int(11) NOT NULL default '0',
      `subscriber_name` varchar(64) NOT NULL default '',
      `subscriber_surname` varchar(64) NOT NULL default '',
      `subscriber_email` varchar(64) NOT NULL default '',
      `confirmed` tinyint(1) NOT NULL default '0',
      `subscribe_date` datetime NOT NULL default '0000-00-00 00:00:00',
      PRIMARY KEY  (`subscriber_id`),
      UNIQUE KEY `subscriber_email` (`subscriber_email`)
    ) ENGINE=MyISAM CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' COMMENT='Subscribers for Comm are stored here.';

如果我在 create table 语句下方添加以下内容,那么我不确定如果该列已经存在(并且可能已填充)会发生什么:

ALTER TABLE `#__comm_subscribers` ADD `subscriber_surname`;
ALTER TABLE `#__comm_subscribers` MODIFY `subscriber_surname` varchar(64) NOT NULL default '';

所以,我尝试了在某处找到的以下内容。这似乎不起作用,但我不完全确定我是否正确使用它。

/*delimiter '//'
CREATE PROCEDURE addcol() BEGIN
IF NOT EXISTS(
SELECT * FROM information_schema.COLUMNS
WHERE COLUMN_NAME='subscriber_surname' AND TABLE_NAME='#__comm_subscribers'
)
THEN
    ALTER TABLE `#__comm_subscribers`
    ADD COLUMN `subscriber_surname` varchar(64) NOT NULL default '';
END IF;
END;
//
delimiter ';'
CALL addcol();
DROP PROCEDURE addcol;*/

有没有人有好的方法来做到这一点?

【问题讨论】:

  • 改变information_schema.COLUMNS,即存储过程的作用,是恕我直言的方法。它的哪一部分“似乎不起作用”?

标签: mysql ddl alter-table


【解决方案1】:

请注意,INFORMATION_SCHEMA 在 5.0 之前的 MySQL 中不受支持。 5.0之前也不支持存储过程,所以如果你需要支持MySQL 4.1,这个方案就不好了。

使用数据库迁移的框架使用的一种解决方案是在您的数据库中记录架构的修订号。只是一个单列单行的表,其中有一个整数表示哪个版本是当前有效的。更新架构时,增加数字。

另一种解决方案是尝试 ALTER TABLE ADD COLUMN 命令。如果该列已经存在,它应该会抛出一个错误。

ERROR 1060 (42S21): Duplicate column name 'newcolumnname'

在升级脚本中发现错误并忽略它。

【讨论】:

  • 好吧,这确实很粗鲁,但总得有人说。如果您只是从命令行运行 SQL 脚本,您可以给 mysql 提供--force 开关,这意味着即使出现错误也要继续运行。那么,就去做吧。如果之前的事情失败了,你只是想确保没有你不想成功的语句。
【解决方案2】:

刚刚尝试了存储过程脚本。似乎问题在于分隔符周围的' 标记。 MySQL Docs 表明分隔符不需要单引号。

所以你想要:

delimiter //

代替:

delimiter '//'

为我工作:)

【讨论】:

  • @Andy 你完全没抓住重点。此评论指出了 OP 在哪里犯了错误。如果不是单引号,OP 就已经存在了。
【解决方案3】:

这是一个可行的解决方案(刚刚在 Solaris 上使用 MySQL 5.0 进行了尝试):

DELIMITER $$

DROP PROCEDURE IF EXISTS upgrade_database_1_0_to_2_0 $$
CREATE PROCEDURE upgrade_database_1_0_to_2_0()
BEGIN

-- rename a table safely
IF NOT EXISTS( (SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE()
        AND TABLE_NAME='my_old_table_name') ) THEN
    RENAME TABLE 
        my_old_table_name TO my_new_table_name,
END IF;

-- add a column safely
IF NOT EXISTS( (SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE()
        AND COLUMN_NAME='my_additional_column' AND TABLE_NAME='my_table_name') ) THEN
    ALTER TABLE my_table_name ADD my_additional_column varchar(2048) NOT NULL DEFAULT '';
END IF;

END $$

CALL upgrade_database_1_0_to_2_0() $$

DELIMITER ;

乍一看它可能看起来比它应该的更复杂,但我们必须在这里处理以下问题:

  • IF 语句仅在存储过程中有效,在直接运行时无效, 例如在 mysql 客户端中
  • 更优雅简洁的SHOW COLUMNS在存储过程中不起作用,所以必须使用INFORMATION_SCHEMA
  • 在 MySQL 中分隔语句的语法很奇怪,所以你必须 重新定义分隔符以便能够创建存储过程。不要 忘记切换分隔符!
  • INFORMATION_SCHEMA 对所有数据库都是全局的,不要忘记 过滤TABLE_SCHEMA=DATABASE()DATABASE() 返回名称 当前选择的数据库。

【讨论】:

  • 希望我能奖励积分,以解释导致这种方法所涉及的问题。谢谢。
【解决方案4】:

另一种方法是使用declare continue handler 忽略错误:

delimiter ;;
create procedure foo ()
begin
    declare continue handler for 1060 begin end;
    alter table atable add subscriber_surname varchar(64);
end;;
call foo();;

我认为这种方式比exists 子查询更简洁。特别是如果您要添加很多列,并且想要多次运行脚本。

更多关于继续处理程序的信息可以在http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html找到

【讨论】:

  • 爱它!永远不会想到这一点。我肯定会改用这种做事方式。
  • 错误 1060 (42S21):重复的列名 'newcolumnname'
  • 这真是太棒了!
【解决方案5】:

如果您在脚本中运行它,您需要在之后添加以下行以使其可重新运行,否则您会收到一个过程已存在错误。

drop procedure foo;

【讨论】:

    【解决方案6】:

    在 PHP > PDO 中添加列的最佳方法:

    $Add = $dbh->prepare("ALTER TABLE `YourCurrentTable` ADD `YourNewColumnName` INT NOT NULL");
    $Add->execute();
    

    注意:表中的列是不可重复的,也就是说我们不需要检查列的存在,但是为了解决问题,我们检查上面的代码:

    例如如果它工作警报1,如果不是0,这意味着该列存在! :)

    【讨论】:

      【解决方案7】:

      检查 PDO 中是​​否存在列 (100%)

      {
          if(isset($_POST['Add']))
          {
              $ColumnExist = $dbh->prepare("SELECT * FROM ColumnChecker where column_name='$insert_column_name' LIMIT 1");
              $ColumnExist ->execute();
              $ColumnName = $ColumnExist->fetch(2);
              $Display_Column_Name = $ColumnName['column_name'];
      
              if($Display_Column_Name == $insert_column_name)
              {
                  echo "$Display_Column_Name already exist";
              } //*****************************
              else 
              {
                  $InsertColumn = $dbh->prepare("insert into ColumnChecker ( column_name ) values ('$insert_column_name')");
                  $InsertColumn->execute();
      
                  if($InsertColumn)
                  {
                      $Add = $dbh->prepare("ALTER TABLE `$Table` ADD `$insert_column_name` $insert_column_type($insert_column_Length) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ");
                      $Add->execute();
      
                      if($Add)
                      {
                          echo 'Table has been updated';  
                      }
                      else 
                      {
                          echo 'Sorry! Try again...'; 
                      }
                  }   
              }
          }
      }#Add Column into Table :)
      

      【讨论】:

        【解决方案8】:

        我使用的是 MySQL 5.5.19。

        我喜欢可以运行和重新运行而不会出现错误的脚本,尤其是在警告似乎持续存在的情况下,稍后在我运行没有错误/警告的脚本时再次出现。至于添加字段,我自己编写了一个程序来减少输入:

        -- add fields to template table to support ignoring extra data 
        -- at the top/bottom of every page
        CALL addFieldIfNotExists ('template', 'firstPageHeaderEndY', 'INT NOT NULL DEFAULT 0');
        CALL addFieldIfNotExists ('template', 'pageHeaderEndY', 'INT NOT NULL DEFAULT 0');
        CALL addFieldIfNotExists ('template', 'pageFooterBeginY', 'INT NOT NULL DEFAULT 792');
        

        创建addFieldIfNotExists过程的代码如下:

        DELIMITER $$
        
        DROP PROCEDURE IF EXISTS addFieldIfNotExists 
        $$
        
        DROP FUNCTION IF EXISTS isFieldExisting 
        $$
        
        CREATE FUNCTION isFieldExisting (table_name_IN VARCHAR(100), field_name_IN VARCHAR(100)) 
        RETURNS INT
        RETURN (
            SELECT COUNT(COLUMN_NAME) 
            FROM INFORMATION_SCHEMA.columns 
            WHERE TABLE_SCHEMA = DATABASE() 
            AND TABLE_NAME = table_name_IN 
            AND COLUMN_NAME = field_name_IN
        )
        $$
        
        CREATE PROCEDURE addFieldIfNotExists (
            IN table_name_IN VARCHAR(100)
            , IN field_name_IN VARCHAR(100)
            , IN field_definition_IN VARCHAR(100)
        )
        BEGIN
        
            -- http://javajon.blogspot.com/2012/10/mysql-alter-table-add-column-if-not.html
        
            SET @isFieldThere = isFieldExisting(table_name_IN, field_name_IN);
            IF (@isFieldThere = 0) THEN
        
                SET @ddl = CONCAT('ALTER TABLE ', table_name_IN);
                SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ;
                SET @ddl = CONCAT(@ddl, ' ', field_name_IN);
                SET @ddl = CONCAT(@ddl, ' ', field_definition_IN);
        
                PREPARE stmt FROM @ddl;
                EXECUTE stmt;
                DEALLOCATE PREPARE stmt;
        
            END IF;
        
        END;
        $$
        

        我没有编写安全修改列的过程,但我认为可以轻松修改上述过程。

        【讨论】:

          【解决方案9】:

          大多数答案都解决了如何在存储过程中安全地添加列,我需要在不使用存储过程的情况下安全地向表中添加列,并发现 MySQL 不允许在外部使用 IF Exists() SP。我将发布我的解决方案,它可能对处于相同情况的人有所帮助。

          SELECT count(*)
          INTO @exist
          FROM information_schema.columns 
          WHERE table_schema = database()
          and COLUMN_NAME = 'original_data'
          AND table_name = 'mytable';
          
          set @query = IF(@exist <= 0, 'alter table intent add column mycolumn4 varchar(2048) NULL after mycolumn3', 
          'select \'Column Exists\' status');
          
          prepare stmt from @query;
          
          EXECUTE stmt;
          

          【讨论】:

          • 注意,当通过 MySQL 工作台 GUI 工作时,我必须在 SELECT 语句中添加一个“LIMIT 1”。
          • 然后在最后DEALLOCATE PREPARE stmt;
          【解决方案10】:

          我采用了 OP 的存储过程并使其可重用且独立于架构。显然它仍然需要 MySQL 5。

          DROP PROCEDURE IF EXISTS AddCol;
          
          DELIMITER //
          
          CREATE PROCEDURE AddCol(
              IN param_schema VARCHAR(100),
              IN param_table_name VARCHAR(100),
              IN param_column VARCHAR(100),
              IN param_column_details VARCHAR(100)
          ) 
          BEGIN
              IF NOT EXISTS(
              SELECT NULL FROM information_schema.COLUMNS
              WHERE COLUMN_NAME=param_column AND TABLE_NAME=param_table_name AND table_schema = param_schema
              )
              THEN
                  set @paramTable = param_table_name ;
                  set @ParamColumn = param_column ;
                  set @ParamSchema = param_schema;
                  set @ParamColumnDetails = param_column_details;
                  /* Create the full statement to execute */
                  set @StatementToExecute = concat('ALTER TABLE `',@ParamSchema,'`.`',@paramTable,'` ADD COLUMN `',@ParamColumn,'` ',@ParamColumnDetails);
                  /* Prepare and execute the statement that was built */
                  prepare DynamicStatement from @StatementToExecute ;
                  execute DynamicStatement ;
                  /* Cleanup the prepared statement */
                  deallocate prepare DynamicStatement ;
          
              END IF;
          END //
          
          DELIMITER ;
          

          【讨论】:

          • 这对我很有效。我必须做的唯一更改是删除对 concat 的调用中的反引号 (`)。此外,可以通过删除变量@paramTable、@ParamColumn、@ParamSchema 和@ParamColumnDetails 来简化代码,直接使用参数。
          【解决方案11】:
          $smpt = $pdo->prepare("SHOW fields FROM __TABLE__NAME__");
          $smpt->execute();
          $res = $smpt->fetchAll(PDO::FETCH_ASSOC);
          //print_r($res);
          

          然后在 $res 中循环查找您的列的键 像这样的:

              if($field['Field'] == '_my_col_'){
                 return true;
              }
          +
          
          **Below code is good for checking column existing in the WordPress tables:**
          public static function is_table_col_exists($table, $col)
              {
                  global $wpdb;
                  $fields = $wpdb->get_results("SHOW fields FROM {$table}", ARRAY_A);
                  foreach ($fields as $field)
                  {
                      if ($field['Field'] == $col)
                      {
                          return TRUE;
                      }
                  }
          
                  return FALSE;
              }
          

          【讨论】:

          • 这可以做得更高效SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; 然后检查结果集是否为非空
          【解决方案12】:
          ALTER TABLE `subscriber_surname` ADD  IF NOT EXISTS  `#__comm_subscribers`.`subscriber_surname`;
          
          ALTER TABLE `#__comm_subscribers` MODIFY `subscriber_surname` varchar(64) NOT NULL default '';
          

          【讨论】:

          • 如果您可以在您的解决方案中添加一些描述,那就太好了,因为我们(用户)可以理解此解决方案的优势,尽管其他解决方案。这是对这个和未来答案的改进。
          【解决方案13】:

          如果您使用 MariaDB,则无需使用存储过程。只需使用,例如:

          ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name tinyint(1) DEFAULT 0;
          

          See here

          【讨论】:

          • 太棒了!使用 MariaDB 的另一个原因。
          • +1 我一直在和 Maria 一起工作,并尝试了上述所有步骤,但在我开始之前,这些步骤都没有奏效,这救了我的命。
          • 有趣.. 我从来没有对 MariaDB 感到难过,这篇文章可以提供一些关于它的想法seravo.fi/2015/…
          • 这个解决方案很棒,但确实需要 MariaDB 10.0.2。对于想要使用这个优雅的解决方案但被困在旧版本的人来说,请注意。
          • ALTER TABLE table_name 如果存在则更改列 column_wrong_name column_name tinyint(1) DEFAULT 0; - 效果也不错!
          【解决方案14】:

          来自 Jake https://stackoverflow.com/a/6476091/6751901 的程序是添加新列的非常简单且良好的解决方案,但多了一行:

          DROP PROCEDURE IF EXISTS foo;;
          

          您可以稍后在此处添加新列,下次也可以使用:

          delimiter ;;
          DROP PROCEDURE IF EXISTS foo;;
          create procedure foo ()
          begin
              declare continue handler for 1060 begin end;
              alter table atable add subscriber_surname varchar(64);
              alter table atable add subscriber_address varchar(254);
          end;;
          call foo();;
          

          【讨论】:

            【解决方案15】:

            以下是 MySQL 中的存储过程,如果数据库表中不存在列,则在不同数据库中的不同表中添加列,具有以下优点

            • 可以一次添加多个列以更改不同数据库中的多个表
            • 三个mysql命令运行,即DROP、CREATE、CALL For Procedure
            • DATABASE 名称应根据 USE 进行更改,否则多个数据可能会出现问题

            DROP PROCEDURE  IF EXISTS `AlterTables`;
            DELIMITER $$
            CREATE PROCEDURE `AlterTables`() 
            BEGIN
                DECLARE table1_column1_count INT;
                DECLARE table2_column2_count INT;
                SET table1_column1_count = (  SELECT COUNT(*) 
                                FROM INFORMATION_SCHEMA.COLUMNS
                                WHERE   TABLE_SCHEMA = 'DATABASE_NAME' AND
            			    TABLE_NAME = 'TABLE_NAME1' AND 
                                        COLUMN_NAME = 'TABLE_NAME1_COLUMN1');
                SET table2_column2_count = (  SELECT COUNT(*) 
                                FROM INFORMATION_SCHEMA.COLUMNS
                                WHERE   TABLE_SCHEMA = 'DATABASE_NAME' AND
            			    TABLE_NAME = 'TABLE_NAME2' AND 
                                        COLUMN_NAME = 'TABLE_NAME2_COLUMN2');
                IF table1_column1_count = 0 THEN
                    ALTER TABLE `TABLE_NAME1`ADD `TABLE_NAME1_COLUMN1` text COLLATE 'latin1_swedish_ci' NULL AFTER `TABLE_NAME1_COLUMN3`,COMMENT='COMMENT HERE';
                END IF;
                IF table2_column2_count = 0 THEN
                    ALTER TABLE `TABLE_NAME2` ADD `TABLE_NAME2_COLUMN2` VARCHAR( 100 ) NULL DEFAULT NULL COMMENT 'COMMENT HERE';
                END IF;
            END $$
            DELIMITER ;
            call AlterTables();

            【讨论】:

              【解决方案16】:

              这是我的 PHP/PDO 解决方案:

              function addColumnIfNotExists($db, $table, $col, $type, $null = true) {
                  global $pdo;
                  if($res = $pdo->query("SHOW COLUMNS FROM `$db`.`$table` WHERE `Field`='$col'")) {
                      if(!$res = $res->fetchAll(PDO::FETCH_ASSOC)) {
                          $null = ($null ? 'NULL' : 'NOT NULL');
                          $pdo->query("ALTER TABLE `$db`.`$table` ADD `$col` $type $null");
                      }
                  }
              }
              

              请注意,它会自动在表格末尾添加列。我还没有实现查询的DEFAULT 部分。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-11-08
                • 1970-01-01
                • 2011-10-30
                • 1970-01-01
                • 1970-01-01
                • 2018-02-02
                相关资源
                最近更新 更多