존재하지 않는 경우 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로 이동하는 방법입니다. 어떤 부분이 "작동하지 않는 것 같습니까"?
rodion 2009-06-09

답변:


49

참고 INFORMATION_SCHEMA이전 5.0에 MySQL을 지원하지 않습니다. 5.0 이전에는 저장 프로 시저가 지원되지 않으므로 MySQL 4.1을 지원해야하는 경우이 솔루션은 좋지 않습니다.

데이터베이스 마이그레이션 을 사용하는 프레임 워크에서 사용하는 한 가지 솔루션 은 스키마의 개정 번호를 데이터베이스에 기록하는 것입니다. 단일 열과 단일 행이있는 테이블이며 현재 유효한 개정을 나타내는 정수가 있습니다. 스키마를 업데이트 할 때 숫자를 늘립니다.

또 다른 해결책은 명령을 시도 하는 것 ALTER TABLE ADD COLUMN입니다. 열이 이미 있으면 오류가 발생합니다.

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

오류를 포착하고 업그레이드 스크립트에서 무시하십시오.


1
OK 이것은 정말 조잡하지만 누군가는 그것을 말해야합니다. 명령 줄에서 단순히 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
이 접근 방식으로 이어진 관련 문제를 설명하면 보너스 포인트를받을 수 있기를 바랍니다. 감사합니다.
Bryan Petty

48

MariaDB를 사용하는 경우 저장 프로 시저를 사용할 필요가 없습니다. 예를 들어 다음을 사용하십시오.

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

여기를 보아라


8
훌륭한! MariaDB를 사용하는 또 다른 이유.
Andrew Ensley

+1 나는 항상 마리아와 함께 일하고 있으며 위의 모든 단계를 시도 해봤습니다.이 단계를 밟기 전까지는 아무도 효과가 없었습니다. 이것은 내 생명을 구했습니다.
Thielicious

흥미로운 .. 나는 MariaDB에 대해 결코 열심히하지 않았고이
walv

이 솔루션은 훌륭하지만 MariaDB 10.0.2가 필요합니다. 이 우아한 솔루션을 사용하고 싶지만 이전 버전에 갇혀있는 사람이라면 누구에게나주의를 기울이십시오.
jblopez

ALTER TABLE table_name CHANGE COLUMN IF EXISTS column_wrong_name column_name tinyint (1) DEFAULT 0; -도 잘 작동합니다!
Damonsson

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"을 추가해야했습니다.
Al Dass 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

ERROR 1060 (42S21) : 중복 열 이름 'newcolumnname'
제이크

이것은 정말 멋지다!
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

1

저장 프로 시저 스크립트를 시도했습니다. 문제는' 구분 기호 주위의 표시입니다. MySQL의 문서는 그 구분 문자는 작은 따옴표가 필요하지 않습니다 보여줍니다.

따라서 원하는 :

delimiter //

대신에:

delimiter '//'

나를 위해 작동합니다 :)


@Andy 당신은 요점을 완전히 놓쳤습니다. 이 의견은 OP가 실수 한 부분을 지적합니다. 작은 따옴표가 아니라면 OP가 이미 있습니다.
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 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;
    }

1
이것은 좀 더 효율적으로 수행 할 수 있으며 SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; 결과 집합이 비어 있지
Eugen Mayer

0

다음은 데이터베이스 테이블에 열이없는 경우 다음과 같은 이점이있는 다른 데이터베이스의 다른 테이블에있는 열을 추가하는 MySQL의 저장 프로 시저입니다.

  • 여러 열을 한 번에 추가하여 다른 데이터베이스의 여러 테이블을 변경할 수 있습니다.
  • 세 개의 mysql 명령이 실행됩니다. 즉, DROP, CREATE, CALL For Procedure
  • 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
우리 (사용자)가 다른 솔루션에도 불구하고이 솔루션의 장점을 이해할 수 있도록 솔루션에 설명을 추가 할 수 있다면 좋을 것입니다. 이것은 현재와 미래의 답변에 대한 개선입니다.
Luís Cruz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.