додайте стовпчик до таблиці 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.