добавить столбец в таблицу MySQL, если он не существует - PullRequest
101 голосов
/ 10 июня 2009

Мои исследования и эксперименты пока не дали ответа, поэтому я надеюсь на некоторую помощь.

Я изменяю установочный файл приложения, в предыдущих версиях которого не было столбца, который я хочу добавить сейчас. Я не хочу добавлять столбец вручную, но в установочном файле и только в том случае, если новый столбец еще не существует в таблице.

Таблица создается следующим образом:

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;*/

У кого-нибудь есть хороший способ сделать это?

Ответы [ 15 ]

80 голосов
/ 11 августа 2010

Вот рабочее решение (только что опробованное с MySQL 5.0 на Solaris):

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() возвращает имя текущая выбранная база данных.
48 голосов
/ 10 июня 2009

Обратите внимание, что INFORMATION_SCHEMA не поддерживается в MySQL до 5.0. Хранимые процедуры также не поддерживаются до 5.0, поэтому, если вам нужно поддерживать MySQL 4.1, это решение не подходит.

Одно решение, используемое платформами, использующими миграцию базы данных , заключается в записи в вашей базе данных номера редакции для схемы. Просто таблица с одним столбцом и одной строкой, с целым числом, указывающим, какая ревизия является действующей. Когда вы обновите схему, увеличьте число.

Другим решением было бы просто попробовать команду ALTER TABLE ADD COLUMN. Должен выдать ошибку, если столбец уже существует.

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

Перехватите ошибку и проигнорируйте ее в вашем скрипте обновления.

32 голосов
/ 15 ноября 2012

Большинство ответов посвящено тому, как безопасно добавить столбец в хранимой процедуре. Мне нужно было безопасно добавить столбец в таблицу, не используя хранимый процесс, и обнаружил, что 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;
31 голосов
/ 17 ноября 2015

Если вы находитесь на MariaDB, нет необходимости использовать хранимые процедуры. Просто используйте, например:

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

Смотрите здесь

22 голосов
/ 25 июня 2011

Другим способом сделать это было бы игнорирование ошибки с 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

6 голосов
/ 22 октября 2012

Я использую 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;
$$

Я не написал процедуру для безопасного изменения столбца, но я думаю, что вышеприведенную процедуру можно легко изменить для этого.

5 голосов
/ 29 марта 2013

Я взял sproc 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 ;
1 голос
/ 24 августа 2016

Процедура от Джейка 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();;
1 голос
/ 16 августа 2012

Проверьте, существует или нет столбец в 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 :)
1 голос
/ 16 августа 2012

Лучший способ добавить колонку в PHP> PDO:

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

Примечание: столбец в таблице не повторяется, это означает, что нам не нужно проверять наличие столбца, но для решения проблемы мы проверяем приведенный выше код:

например, если работает оповещение 1, если не 0, то есть столбец существует! :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...