додайте стовпчик до таблиці mysql, якщо він не існує


109

Мої дослідження та експерименти ще не дали відповіді, тому я сподіваюся на допомогу.

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

Таблиця створюється наступним чином:

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.';

Якщо я додаю наступне під оператором створення таблиці, я не впевнений, що станеться, якщо стовпець вже існує (і, можливо, заповнений):

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

Хтось має хороший спосіб це зробити?


2
Змінення information_schema.COLUMNS, тобто що зберігається процедура, - це спосіб перейти до IMHO. Яка його частина "начебто не працює"?
родіон

Відповіді:


49

Зауважте, що INFORMATION_SCHEMAне підтримується в MySQL до 5.0. Також не підтримуються збережені процедури до 5,0, тому якщо вам потрібно підтримувати MySQL 4.1, це рішення не є гарним.

Одне рішення, використовуване рамками, які використовують міграцію баз даних, - це записати у свою базу даних номер редакції для схеми. Просто таблиця з одним стовпцем і одним рядком, з цілим числом, яке вказує, яке перегляд є чинним. Коли ви оновлюєте схему, збільште число.

Іншим рішенням було б просто спробувати на ALTER TABLE ADD COLUMNкоманду. Він повинен видавати помилку, якщо стовпець вже існує.

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

Ловіть помилку та ігноруйте її у своєму сценарії оновлення.


1
Добре, це дійсно грубо, але хтось повинен це сказати. якщо ви просто запускаєте скрипт SQL з командного рядка, ви можете надати mysql --forceперемикач, що означає продовжувати роботу, навіть якщо є помилка. тоді просто займіться цим. ви просто хочете бути впевнені, що немає тверджень, які НЕ хочете досягти успіху, якщо щось попереднє не вдалося.
Девід

85

Ось робоче рішення (щойно випробувано з 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()повертає ім'я поточно вибраної бази даних.

1
Бажаю, щоб я міг присвоїти бонусні бали за пояснення проблем, які призвели до такого підходу. Дякую.
Брайан Петті

48

Якщо ви перебуваєте на MariaDB, не потрібно використовувати збережені процедури. Просто використовуйте, наприклад:

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

Дивіться тут


8
Блискуче! Ще одна причина використовувати MariaDB.
Ендрю Енслі

+1 Я весь час працював з Марією і випробовував усі ці кроки над жодним з них, не працював, поки я не потрапив на цей, це врятувало мені життя.
Тиелісі

Цікаво .. Мені ніколи не важко про MariaDB, і ця стаття може дати деяке уявлення про це seravo.fi/2015/…
walv

Це рішення є чудовим, але вимагає MariaDB 10.0.2. Просто голова для тих, хто хоче скористатися цим елегантним рішенням, але він затримався на більш старій версії.
jblopez

ALTER TABLE table_name CHANGE COLUMN IF EXISTS column_wrong_name column_name tinyint (1) DEFAULT 0; -добре теж добре!
Дамонсон

34

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

1
Зауважте, що мені довелося додати "LIMIT 1" до оператора SELECT під час роботи через графічний інтерфейс MySQL Workbench.
Аль-Дасс

23

Ще один спосіб зробити це - ігнорувати помилку за допомогою 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): дублювання назви стовпців "нове ім'я стовпця"
Джейк

Це справді чудово!
ATOzTOA

6

Я використовую 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

Я взяв ролик 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 ;

Це добре працює для мене. Єдина зміна, яку я повинен був внести, - це видалити зворотні лапки (`) у заклику до конфактування. Також можна спростити код, видаливши змінні @paramTable, @ParamColumn, @ParamSchema та @ParamColumnDetails і просто скористайтеся параметрами безпосередньо.
hrabinowitz

1

Просто спробував збережений сценарій процедури. Здається, проблема - це 'позначки навколо роздільників. В MySQL Docs показують , що символи - обмежувачі не потрібні одиничні лапки.

Отже, ви хочете:

delimiter //

Замість:

delimiter '//'

Для мене працює :)


@Andy Ви повністю пропустили справу. Цей коментар вказує, де ОП допустила свою помилку. ОП вже там, якщо не за цитатами.
RichardTheKiwi

1

Якщо ви запускаєте це в сценарії, вам потрібно буде потім додати наступний рядок, щоб зробити його повторним, інакше ви отримаєте помилку процедури, яка вже існує.

drop procedure foo;

1

Найкращий спосіб додати стовпець у PHP> PDO:

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

Примітка: стовпець у таблиці не повторюється, це означає, що нам не потрібно перевіряти існування стовпця, але для вирішення проблеми перевіряємо наведений вище код:

наприклад, якщо він працює сповіщення 1, якщо не 0, це означає, що стовпець існує! :)


1

Перевірте, чи існує колонка в 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

Процедура від 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();;

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

Тоді в $ res за циклом шукайте ключ вашого стовпця 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;
    }

1
це можна зробити більш ефективно, SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; а потім перевірити, чи не буде порожнім результат
Eugen Mayer

0

Нижче наведено збережена процедура в MySQL для додавання стовпців (ів) у різні таблиці (таблиці) в різних базах даних, якщо стовпець не існує в таблиці (их) баз даних із наступними перевагами

  • можна використовувати декілька стовпців одночасно для зміни декількох таблиць у різних базах даних
  • три команди mysql, тобто DROP, CREATE, CALL для процедури
  • Назва 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();


-1
ALTER TABLE `subscriber_surname` ADD  IF NOT EXISTS  `#__comm_subscribers`.`subscriber_surname`;

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

4
Було б чудово, якби ви могли додати якийсь опис до свого рішення, тому ми (користувачі) могли зрозуміти переваги цього рішення, незважаючи на інші. Це вдосконалення для цієї та майбутніх відповідей.
Луїс Крус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.