ChatGPT解决这个技术问题 Extra ChatGPT

如果它不存在,则将列添加到 mysql 表

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

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

创建表如下:

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,即存储过程的作用,是恕我直言的方法。它的哪一部分“似乎不起作用”?

f
fancyPants

这是一个可行的解决方案(刚刚在 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() 返回当前选定数据库的名称。


希望我能奖励积分,以解释导致这种方法所涉及的问题。谢谢你。
g
gdm

如果您在 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 CHANGE COLUMN IF EXISTS column_wrong_name column_name tinyint(1) DEFAULT 0; - 效果也不错!
B
Bill Karwin

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

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

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

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

在升级脚本中捕获错误并忽略它。


好吧,这真的很粗糙,但有人必须说出来。如果您只是从命令行运行 SQL 脚本,您可以给 mysql 提供 --force 开关,这意味着即使出现错误也继续运行。那么,就去做吧。如果之前的事情失败了,你只想确保没有你不想成功的语句。
J
Jay

大多数答案都解决了如何在存储过程中安全地添加列,我需要在不使用存储过程的情况下安全地将列添加到表中,并发现 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;
J
Jake

另一种方法是使用 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'
这真的很漂亮!
J
Jonathan

我正在使用 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;
$$

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


T
Thomas Paine

我采用了 OP 的 sproc 并使其可重用且独立于模式。显然它仍然需要 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 来简化代码,直接使用参数。
B
Boy Who Roared

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

所以你要:

delimiter //

代替:

delimiter '//'

为我工作:)


@Andy您完全错过了重点。此评论指出了 OP 在哪里犯了错误。如果不是单引号,OP 就已经存在了。
T
Tim Cooper

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

drop procedure foo;

M
Maher

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

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

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

例如,如果它工作警报 1,如果不是 0,则表示该列存在! :)


A
Adi

检查 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 :)

C
Community

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();;

r
realmag777
$smpt = $pdo->prepare("SHOW fields FROM __TABLE__NAME__");
$smpt->execute();
$res = $smpt->fetchAll(PDO::FETCH_ASSOC);
//print_r($res);

然后在 $res by cycle 中查找列 Smth 的键,如下所示:

    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_'; ,然后检查结果集是否为非空
A
Abdul Rehman

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

可以添加多个列一次使用以更改不同数据库中的多个表

三个mysql命令运行,即DROP、CREATE、CALL For Procedure

数据库名称应根据 USE 进行更改,否则可能会出现多个数据的问题

如果存在“AlterTables”,则删除程序; DELIMITER $$ CREATE PROCEDURE `AlterTables`() BEGIN DECLARE table1_column1_count INT;声明 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';万一; IF table2_column2_count = 0 THEN ALTER TABLE `TABLE_NAME2` ADD `TABLE_NAME2_COLUMN2` VARCHAR( 100 ) NULL DEFAULT NULL COMMENT 'COMMENT HERE';万一;结束 $$ 分隔符;调用 AlterTables();


M
Meloman

这是我的 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");
        }
    }
}

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


N
Nazik
ALTER TABLE `subscriber_surname` ADD  IF NOT EXISTS  `#__comm_subscribers`.`subscriber_surname`;

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

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