存在しない場合は、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.';

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

誰かがこれを行う良い方法はありますか?


2
information_schema.COLUMNSを変更すること、つまりストアドプロシージャが行うことは、IMHOへの道です。「動作しないようです」のどの部分ですか?
ロディオン2009年

回答:


49

INFORMATION_SCHEMA5.0より前のMySQLではサポートされていないことに注意してください。また、5.0より前のストアドプロシージャもサポートされていないため、MySQL 4.1をサポートする必要がある場合、このソリューションは適切ではありません。

データベースの移行を使用するフレームワークで使用される1つの解決策は、スキーマのリビジョン番号をデータベースに記録することです。単一の列と単一の行を持つ単なるテーブルで、現在有効なリビジョンを示す整数が含まれています。スキーマを更新するときに、数値を増やします。

別の解決策は、コマンドを試してみることALTER TABLE ADD COLUMNです。列がすでに存在する場合は、エラーがスローされます。

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

エラーをキャッチし、アップグレードスクリプトで無視します。


1
わかりました、これは本当に大雑把ですが、誰かがそれを言わなければなりません。コマンドラインから単にSQLスクリプトを実行している場合は、mysqlに--forceスイッチを指定できます。これは、エラーが発生しても続行することを意味します。その後、それのために行きます。以前の何かが失敗した場合に成功したくないステートメントがないことを確認したいだけです。
David

85

これは実用的なソリューションです(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()現在選択されているデータベースの名前を返します。

1
このアプローチにつながった問題を説明するためにボーナスポイントを付与できるといいのですが。ありがとうございました。
ブライアンペティ

48

MariaDBを使用している場合は、ストアドプロシージャを使用する必要はありません。たとえば、次のように使用します。

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

こちらをご覧ください


8
鮮やかさ!MariaDBを使用するもう1つの理由。
Andrew Ensley

+1私はいつもマリアと仕事をしていて、上記のすべての手順を試してみましたが、この手順にたどり着くまではどれもうまくいきませんでした。これは私の命を救いました。
16

面白い..私は決してハードMariaDBこの記事についてそれについていくつかのアイデアを与えることができますseravo.fi/2015/...
walv

このソリューションは素晴らしいですが、MariaDB 10.0.2が必要です。このエレガントなソリューションを使用したいが、古いバージョンでは立ち往生している人のために準備を整えます。
jblopez

ALTER TABLE table_name変更列が存在する場合column_wrong_name column_name tinyint(1)DEFAULT 0; -うまくいきます!
デイモンソン

34

ほとんどの回答は、ストアドプロシージャに列を安全に追加する方法を扱っています。ストアドプロシージャを使用せずに列をテーブルに安全に追加する必要があり、MySQLではSPIF Exists()外部での使用が許可されていないことがわかりました。同じ状況の誰かを助けるかもしれないという私の解決策を投稿します。

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
MySQLワークベンチGUIを介して作業する場合、SELECTステートメントに「LIMIT 1」を追加する必要があることに注意してください。
アルダス2014年

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を参照してください。


大好きです!それを考えたことはなかったでしょう。確かにこの方法に切り替えるつもりです。
Johnny Kauffman

重複する列名'newcolumnname':ERROR 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の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を削除してコードを単純化し、パラメーターを直接使用することもできます。
hrabinowitz 2013

1

ストアドプロシージャスクリプトを試しました。問題は'区切り文字の周りのマークにあるようです。MySQLのドキュメントには、区切り文字は、単一引用符を必要としないことを示しています。

あなたが望んでいるのは:

delimiter //

の代わりに:

delimiter '//'

私のために働く:)


@Andyあなたは完全にポイントを逃した。このコメントは、OPがどこでミスをしたかを指摘しています。OPは、シングルクォートでない場合、すでにそこにありました。
RichardTheKiwi 2012

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の手順は、新しい列を追加するための非常にシンプルで優れたソリューションですが、1行追加するだけです。

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のストアドプロシージャであり、次の利点があります。

  • 複数の列を一度に追加して、異なるデータベースの複数のテーブルを変更できます。
  • 3つのmysqlコマンドが実行されます。つまり、DROP、CREATE、CALL Forプロシージャ
  • データベース名は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
私たち(ユーザー)が他のソリューションにかかわらずこのソリューションの利点を理解できるように、ソリューションに説明を追加できればすばらしいと思います。これは、現在および将来の回答の改善です。
ルイス・クルーズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.