thêm cột vào bảng mysql nếu nó không tồn tại


109

Nghiên cứu và thử nghiệm của tôi vẫn chưa mang lại câu trả lời, vì vậy tôi hy vọng được giúp đỡ.

Tôi đang sửa đổi tệp cài đặt của một ứng dụng mà trong các phiên bản trước không có cột mà tôi muốn thêm ngay bây giờ. Tôi không muốn thêm cột theo cách thủ công, nhưng trong tệp cài đặt và chỉ khi cột mới chưa tồn tại trong bảng.

Bảng được tạo như sau:

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

Nếu tôi thêm phần sau, bên dưới câu lệnh tạo bảng, thì tôi không chắc điều gì sẽ xảy ra nếu cột đã tồn tại (và có lẽ đã được điền):

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

Vì vậy, tôi đã thử cách sau mà tôi tìm thấy ở đâu đó. Điều này dường như không hiệu quả nhưng tôi không hoàn toàn chắc chắn rằng tôi đã sử dụng nó đúng cách.

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

Có ai có một cách tốt để làm điều này?


2
Thay đổi information_schema.COLUMNS, tức là những gì thủ tục được lưu trữ thực hiện, là cách để truy cập IMHO. Phần nào của nó "dường như không hoạt động"?
rodion

Câu trả lời:


49

Lưu ý rằng điều đó INFORMATION_SCHEMAkhông được hỗ trợ trong MySQL trước 5.0. Các thủ tục được lưu trữ cũng không được hỗ trợ trước 5.0, vì vậy nếu bạn cần hỗ trợ MySQL 4.1, giải pháp này không tốt.

Một giải pháp được sử dụng bởi các khuôn khổ sử dụng di chuyển cơ sở dữ liệu là ghi lại trong cơ sở dữ liệu của bạn một số sửa đổi cho lược đồ. Chỉ là một bảng có một cột và một hàng, với một số nguyên cho biết bản sửa đổi nào đang có hiệu lực. Khi bạn cập nhật giản đồ, hãy tăng số lượng.

Một giải pháp khác sẽ được chỉ thử các ALTER TABLE ADD COLUMNlệnh. Nó sẽ xuất hiện một lỗi nếu cột đã tồn tại.

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

Bắt lỗi và bỏ qua lỗi đó trong tập lệnh nâng cấp của bạn.


1
OK, điều này thực sự thô thiển nhưng ai đó phải nói điều đó. nếu bạn chỉ đang chạy một tập lệnh SQL từ dòng lệnh, bạn có thể cung cấp cho mysql --forcechuyển đổi, có nghĩa là tiếp tục chạy ngay cả khi có lỗi. sau đó, chỉ cần đi cho nó. bạn chỉ muốn chắc chắn rằng không có câu nào mà bạn KHÔNG muốn thành công nếu điều gì đó trước đó đã thất bại.
David

85

Đây là một giải pháp hoạt động (vừa được dùng thử với MySQL 5.0 trên 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 ;

Thoạt nhìn, nó có vẻ phức tạp hơn bình thường, nhưng chúng ta phải giải quyết các vấn đề sau đây:

  • IF câu lệnh chỉ hoạt động trong các thủ tục được lưu trữ, không hoạt động khi chạy trực tiếp, ví dụ: trong máy khách mysql
  • thanh lịch và ngắn gọn hơn SHOW COLUMNSkhông hoạt động trong quy trình được lưu trữ, vì vậy phải sử dụng INFORMATION_SCHEMA
  • cú pháp để phân tách các câu lệnh là lạ trong MySQL, vì vậy bạn phải xác định lại dấu phân cách để có thể tạo các thủ tục được lưu trữ. Đừng quên chuyển dấu phân cách trở lại!
  • INFORMATION_SCHEMA là toàn cầu cho tất cả cơ sở dữ liệu, đừng quên lọc trên TABLE_SCHEMA=DATABASE(). DATABASE()trả về tên của cơ sở dữ liệu hiện được chọn.

1
Ước gì tôi có thể trao điểm thưởng cho việc giải thích các vấn đề liên quan dẫn đến cách tiếp cận này. Cảm ơn bạn.
Bryan Petty

48

Nếu bạn đang sử dụng MariaDB, không cần sử dụng các thủ tục được lưu trữ. Chỉ cần sử dụng, ví dụ:

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

Xem tại đây


8
Xuất sắc! Tuy nhiên, một lý do khác để sử dụng MariaDB.
Andrew Ensley

+1 Tôi đã làm việc với Maria mọi lúc và thử tất cả các bước trên không có bước nào hiệu quả cho đến khi tôi thực hiện bước này, điều này đã cứu mạng tôi.
Thielicious

Thật thú vị .. Tôi không bao giờ khó hiểu về MariaDB và bài viết này có thể cung cấp một số ý tưởng về nó seravo.fi/2015/…
walv

Giải pháp này là tuyệt vời, nhưng yêu cầu MariaDB 10.0.2. Thông báo cho bất kỳ ai muốn sử dụng giải pháp thanh lịch này, nhưng bị mắc kẹt trên phiên bản cũ hơn.
jblopez

ALTER TABLE tên_bảng THAY ĐỔI CỘT NẾU TỒN TẠI column_wrong_name column_name tinyint (1) DEFAULT 0; -cũng tốt!
Damonsson

34

Hầu hết các câu trả lời đề cập đến cách thêm một cột một cách an toàn trong một thủ tục được lưu trữ, tôi có nhu cầu thêm một cột vào bảng một cách an toàn mà không cần sử dụng một proc được lưu trữ và phát hiện ra rằng MySQL không cho phép sử dụng IF Exists()bên ngoài một SP . Tôi sẽ đăng giải pháp của mình rằng nó có thể giúp ích cho ai đó trong hoàn cảnh tương tự.

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
Lưu ý rằng tôi đã phải thêm "LIMIT 1" vào câu lệnh SELECT khi làm việc thông qua GUI bàn làm việc MySQL.
Al Dass

23

Một cách khác để làm điều này là bỏ qua lỗi với 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();;

Tôi nghĩ nó gọn gàng hơn theo cách này so với existstruy vấn con. Đặc biệt nếu bạn có nhiều cột để thêm và bạn muốn chạy tập lệnh nhiều lần.

có thể tìm thấy thêm thông tin về trình xử lý tiếp tục tại http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html


Yêu nó! Không bao giờ có thể nghĩ đến điều đó. Tôi chắc chắn sẽ chuyển sang cách làm này.
Johnny Kauffman

LỖI 1060 (42S21): Tên cột trùng lặp 'newcolumnname'
Jake

Điều này thực sự tiện lợi!
ATOzTOA

6

Tôi đang sử dụng MySQL 5.5.19.

Tôi thích có các tập lệnh mà bạn có thể chạy và chạy lại mà không bị lỗi, đặc biệt là những nơi cảnh báo dường như kéo dài, hiển thị lại sau khi tôi đang chạy tập lệnh không có lỗi / cảnh báo. Khi thêm các trường vào, tôi đã tự viết cho mình một thủ tục để giảm bớt việc gõ một chút:

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

Mã để tạo thủ tục addFieldIfNotExists như sau:

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;
$$

Tôi đã không viết một quy trình để sửa đổi một cột một cách an toàn, nhưng tôi nghĩ rằng quy trình trên có thể dễ dàng sửa đổi để làm như vậy.


5

Tôi đã lấy mầm của OP và làm cho nó có thể tái sử dụng và lược đồ độc lập. Rõ ràng là nó vẫn yêu cầu 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 ;

Công việc này tốt cho tôi. Thay đổi duy nhất mà tôi phải thực hiện là loại bỏ dấu ngoặc kép (`) trong lệnh gọi nối. Ngoài ra, có thể đơn giản hóa mã bằng cách loại bỏ các biến @paramTable, @ParamColumn, @ParamSchema và @ParamColumnDetails và chỉ sử dụng trực tiếp các tham số.
hrabinowitz

1

Chỉ cần thử tập lệnh thủ tục được lưu trữ. Có vẻ vấn đề là các 'dấu xung quanh các dấu phân cách. Các MySQL Documents cho thấy nhân vật delimiter không cần dấu nháy đơn.

Vậy bạn muốn:

delimiter //

Thay vì:

delimiter '//'

Làm việc cho tôi :)


@Andy Bạn hoàn toàn bỏ sót điểm. Nhận xét này chỉ ra nơi OP đã mắc sai lầm. OP đã ở đó nếu không có các dấu ngoặc kép.
RichardTheKiwi

1

Nếu bạn đang chạy điều này trong một tập lệnh, bạn sẽ muốn thêm dòng sau để làm cho nó có thể chạy lại, nếu không, bạn sẽ gặp lỗi thủ tục đã tồn tại.

drop procedure foo;

1

Cách tốt nhất để thêm cột trong PHP> PDO:

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

Lưu ý: cột trong bảng không thể lặp lại, điều đó có nghĩa là chúng tôi không cần kiểm tra sự tồn tại của một cột, nhưng để giải quyết vấn đề, chúng tôi kiểm tra đoạn mã trên:

Ví dụ: nếu nó hoạt động, cảnh báo 1, nếu không phải 0, có nghĩa là cột tồn tại! :)


1

Kiểm tra xem cột có tồn tại hay không trong 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

Thủ tục từ Jake https://stackoverflow.com/a/6476091/6751901 là giải pháp rất đơn giản và tốt để thêm cột mới, nhưng với một dòng bổ sung:

DROP PROCEDURE IF EXISTS foo;;

bạn có thể thêm các cột mới sau đó ở đó và nó cũng sẽ hoạt động vào lần sau:

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);

Sau đó, trong $ res theo chu kỳ, hãy tìm khóa của cột Smth của bạn như sau:

    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
điều này có thể được thực hiện một litle hiệu quả hơn SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; và sau đó kiểm tra các tập kết quả là không có sản phẩm nào
Eugen Mayer

0

Dưới đây là quy trình Đã lưu trữ trong MySQL Để Thêm (các) Cột trong (các) Bảng khác nhau trong (các) Cơ sở dữ liệu khác nhau nếu cột không tồn tại trong (các) Bảng Cơ sở dữ liệu với các ưu điểm sau

  • nhiều cột có thể được thêm vào sử dụng cùng một lúc để thay đổi nhiều Bảng trong các Cơ sở dữ liệu khác nhau
  • ba lệnh mysql chạy, tức là DROP, CREATE, CALL cho thủ tục
  • CƠ SỞ DỮ LIỆU Tên phải được thay đổi theo SỬ DỤNG, nếu không sự cố có thể xảy ra cho nhiều dữ liệu

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
Sẽ thật tuyệt nếu bạn có thể thêm một số mô tả vào giải pháp của mình vì chúng tôi (người dùng) có thể hiểu được những ưu điểm của giải pháp này mặc dù những giải pháp khác. Đây là một cải tiến cho câu trả lời này và trong tương lai.
Luís Cruz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.