yoksa mysql tablosuna sütun ekle


109

Araştırmalarım ve deneylerim henüz bir cevap vermedi, bu yüzden biraz yardım umuyorum.

Önceki sürümlerde şimdi eklemek istediğim bir sütunu olmayan bir uygulamanın kurulum dosyasını değiştiriyorum. Sütunu manuel olarak eklemek istemiyorum, ancak kurulum dosyasında ve yalnızca tabloda yeni sütun yoksa.

Tablo şu şekilde oluşturulur:

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 ifadesinin altına şunu eklersem, sütun zaten mevcutsa (ve belki de doldurulmuşsa) ne olacağından emin değilim:

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

Bu yüzden, bir yerde bulduğum aşağıdakileri denedim. Bu işe yaramıyor gibi görünüyor ama doğru kullandığımdan tam olarak emin değilim.

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

Bunu yapmanın iyi bir yolu olan var mı?


2
Information_schema.COLUMNS değerini değiştirmek, yani saklı yordamın yaptığı şey, IMHO'ya geçmenin yoludur. Hangi kısmı "işe yaramıyor"?
rodion

Yanıtlar:


49

INFORMATION_SCHEMA5.0'dan önceki MySQL'de desteklenmediğini unutmayın . 5.0'dan önce saklı yordamlar da desteklenmez, bu nedenle MySQL 4.1'i desteklemeniz gerekiyorsa bu çözüm iyi değildir.

Veritabanı geçişlerini kullanan çerçeveler tarafından kullanılan bir çözüm , veritabanınıza şema için bir revizyon numarası kaydetmektir. Hangi revizyonun geçerli olduğunu gösteren bir tam sayıya sahip tek sütunlu ve tek satırlı bir tablo. Şemayı güncellediğinizde sayıyı artırın.

Başka bir çözüm sadece olacaktır deneyinALTER TABLE ADD COLUMN komutu. Sütun zaten mevcutsa bir hata atmalıdır.

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

Hatayı yakalayın ve yükseltme komut dosyanızda dikkate almayın.


1
Tamam, bu gerçekten kaba ama birinin söylemesi gerek. Komut satırından basitçe bir SQL betiği çalıştırıyorsanız, mysql'e --forceanahtarı verebilirsiniz , bu da bir hata olsa bile devam etmeniz anlamına gelir. o zaman, sadece bunun için gidin. sadece, önceki bir şey başarısız olduysa, başarılı olmak istemediğiniz hiçbir ifade olmadığından emin olmak istersiniz.
David

85

İşte çalışan bir çözüm (Solaris'te MySQL 5.0 ile denendi):

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 ;

İlk bakışta muhtemelen olması gerekenden daha karmaşık görünebilir, ancak burada aşağıdaki sorunları ele almalıyız:

  • IF ifadeler yalnızca saklı yordamlarda çalışır, doğrudan çalıştırıldığında değil, örneğin mysql istemcisinde
  • daha zarif ve öz SHOW COLUMNS, saklı yordamda çalışmaz, bu nedenle INFORMATION_SCHEMA kullanmak zorunda
  • MySQL'de sınırlama ifadeleri için sözdizimi gariptir, bu nedenle saklı yordamları oluşturabilmek için sınırlayıcıyı yeniden tanımlamanız gerekir. Sınırlayıcıyı geri değiştirmeyi unutmayın!
  • INFORMATION_SCHEMA tüm veritabanları için globaldir, filtrelemeyi unutmayın TABLE_SCHEMA=DATABASE(). DATABASE()seçili olan veritabanının adını döndürür.

1
Keşke bu yaklaşıma yol açan ilgili konuları açıkladığım için bonus puanlar verebilseydim. Teşekkür ederim.
Bryan Petty

48

MariaDB üzerindeyseniz, saklı prosedürleri kullanmanıza gerek yoktur. Örneğin şunu kullanın:

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

Buraya bakın


8
Parlak! MariaDB'yi kullanmak için başka bir neden.
Andrew Ensley

+1 Her zaman Maria ile çalışıyordum ve yukarıdaki adımların hepsini deniyorum, bu adımı atana kadar hiçbiri işe yaramadı, bu benim hayatımı kurtardı.
Thielicious

İlginç .. MariaDB'yi
walv

Bu çözüm harika, ancak MariaDB 10.0.2 gerektiriyor. Bu zarif çözümü kullanmak isteyen, ancak daha eski bir sürüme takılıp kalan herkese dikkat edin.
jblopez

ALTER TABLE table_name VARSA SÜTUNU DEĞİŞTİR sütun_wrong_name column_name tinyint (1) DEFAULT 0; -işler de iyi!
Damonsson

35

Yanıtların çoğu, saklı yordamda bir sütunun nasıl güvenli bir şekilde ekleneceğine yöneliktir, depolanmış bir proc kullanmadan bir tabloya güvenli bir şekilde sütun ekleme ihtiyacım vardı ve MySQL'in IF Exists()bir SP dışında kullanımına izin vermediğini keşfettim . Aynı durumda birine yardımcı olabileceğine dair çözümümü yayınlayacağım.

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
Not MySQL çalışma tezgahı GUI'si üzerinden çalışırken SELECT deyimine bir "LIMIT 1" eklemem gerekiyordu.
Al Dass

23

Bunu yapmanın başka bir yolu, aşağıdaki hatayı göz ardı etmektir 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();;

Bence bu şekilde bir existsalt sorudan daha temiz . Özellikle ekleyeceğiniz çok sayıda sütununuz varsa ve betiği birkaç kez çalıştırmak istiyorsanız.

Devam işleyicileri hakkında daha fazla bilgi http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html adresinde bulunabilir.


Sevdim! Bunu asla düşünmezdim. Kesinlikle işleri bu şekilde yapacağım.
Johnny Kauffman

HATA 1060 (42S21): Yinelenen sütun adı 'yeni sütun adı'
Jake

Bu gerçekten harika!
ATOzTOA

6

MySQL 5.5.19 kullanıyorum.

Hatasız olarak çalıştırabileceğiniz ve yeniden çalıştırabileceğiniz komut dosyalarına sahip olmayı seviyorum, özellikle de uyarıların oyalandığı durumlarda, hata / uyarı içermeyen komut dosyalarını çalıştırırken daha sonra tekrar görüntüleniyor. Alan eklemeye gelince, biraz daha az yazarak kendime bir prosedür yazdım:

-- 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 prosedürünü oluşturma kodu aşağıdaki gibidir:

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

Bir sütunu güvenli bir şekilde değiştirmek için bir prosedür yazmadım, ancak yukarıdaki prosedürün bunu yapmak için kolayca değiştirilebileceğini düşünüyorum.


5

OP'nin sproc'unu aldım ve yeniden kullanılabilir ve şemadan bağımsız hale getirdim. Açıkçası hala MySQL 5 gerektiriyor.

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 ;

Bu benim için iyi çalışıyor. Yapmam gereken tek değişiklik concat çağrısındaki arka tırnak işaretlerini (`) kaldırmak oldu. Ayrıca, @paramTable, @ParamColumn, @ParamSchema ve @ParamColumnDetails değişkenlerini kaldırarak kodu basitleştirebilir ve yalnızca parametreleri doğrudan kullanabilirsiniz.
hrabinowitz

1

Depolanan yordam komut dosyasını denedim. Görünüşe göre sorun, 'sınırlayıcıların etrafındaki işaretler. MySQL Dokümanlar o sınırlayıcı karakterleri tek tırnak gerekmez göstermektedir.

Yani istiyorsun:

delimiter //

Onun yerine:

delimiter '//'

Benim için çalışıyor :)


@Andy Noktayı tamamen kaçırdınız. Bu yorum, OP'nin hatasını nerede yaptığına işaret ediyor. Tek tırnak için olmasa bile OP zaten oradaydı.
RichardTheKiwi

1

Eğer bunu bir komut dosyasında çalıştırıyorsanız, tekrar kurulabilir hale getirmek için daha sonra aşağıdaki satırı eklemek isteyeceksiniz, aksi takdirde bir prosedür zaten mevcut hatası alırsınız.

drop procedure foo;

1

Sütunu PHP> PDO'ya eklemenin en iyi yolu:

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

Not: Tablodaki sütun tekrarlanabilir değildir, bu, bir sütunun varlığını kontrol etmemiz gerekmediği anlamına gelir, ancak sorunu çözmek için yukarıdaki kodu kontrol ederiz:

örneğin, 0 değilse, 1 numaralı uyarı çalışıyorsa, bu sütunun var olduğu anlamına gelir! :)


1

Kolonun PDO'da var olup olmadığını kontrol edin (% 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'den gelen prosedür, yeni sütunlar eklemek için çok basit ve iyi bir çözümdür, ancak ek bir satırla:

DROP PROCEDURE IF EXISTS foo;;

daha sonra oraya yeni sütunlar ekleyebilirsiniz ve bir dahaki sefere de çalışacaktır:

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

Daha sonra $ res'de döngü ile sütununuzun Smth anahtarını şu şekilde arayın:

    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
bu biraz daha verimli yapılabilir SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; ve ardından sonuç kümesinin boş olmadığını kontrol edin
Eugen Mayer

0

Aşağıda, aşağıdaki avantajları olan bir Veritabanı (ları) Tablosunda / Tablolarında sütun yoksa, farklı Veritabanlarında farklı Tablolarda Sütun (lar) eklemek için MySQL'de Depolanan Prosedür bulunmaktadır.

  • Farklı Veritabanlarında birden çok Tabloyu değiştirmek için aynı anda birden çok sütun eklenebilir
  • üç mysql komutu çalıştırılır, yani DROP, CREATE, CALL For Procedure
  • VERİTABANI Adı, KULLANIMA göre değişiklik göstermelidir, aksi takdirde birden fazla veri için sorun oluşabilir

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
Çözümünüze bir açıklama ekleyebilirseniz harika olurdu, çünkü biz (kullanıcılar) diğerlerine rağmen bu çözümün avantajlarını anlayabiliriz. Bu, bu ve gelecekteki cevaplar için bir gelişmedir.
Luís Cruz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.