agregar columna a la tabla mysql si no existe


Mis investigaciones y experimentos aún no han dado una respuesta, así que espero algo de ayuda.

Estoy modificando el archivo de instalación de una aplicación que en versiones anteriores no tenía una columna que quiero agregar ahora. No quiero añadir la columna manualmente, sino en el archivo de instalación y solo si la nueva columna no existe ya en la tabla.

La tabla se crea de la siguiente manera:

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

Si añado lo siguiente, debajo de la instrucción create table, entonces estoy no estoy seguro de lo que sucede si la columna ya existe (y tal vez está poblada):

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

Entonces, probé lo siguiente que encontré en algún lugar. Esto no parece funcionar, pero no estoy completamente seguro de que lo usé correctamente.

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

¿alguien tiene una buena manera de hacer esto?

 94
Author: markus, 2009-06-10

15 answers

Tenga en cuenta que INFORMATION_SCHEMA no es compatible con MySQL antes de la versión 5.0. Tampoco se admiten procedimientos almacenados anteriores a la versión 5.0, por lo que si necesita admitir MySQL 4.1, esta solución no es buena.

Una solución utilizada por los frameworks que usan migraciones de base de datos es registrar en su base de datos un número de revisión para el esquema. Solo una tabla con una sola columna y una sola fila, con un entero que indica qué revisión está vigente. Al actualizar el esquema, aumente la numero.

Otra solución sería simplemente probar el comando ALTER TABLE ADD COLUMN. Debería lanzar un error si la columna ya existe.

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

Capte el error e ignórelo en su script de actualización.

 45
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-09 23:43:17

Aquí está una solución de trabajo (acaba de probar con MySQL 5.0 en 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 ;

A primera vista probablemente parezca más complicado de lo que debería, pero tenemos que lidiar con los siguientes problemas aquí:

  • IF las sentencias solo funcionan en procedimientos almacenados, no cuando se ejecutan directamente, por ejemplo, en mysql client
  • más elegante y conciso SHOW COLUMNS no funciona en el procedimiento almacenado por lo que tiene que utilizar INFORMATION_SCHEMA
  • la sintaxis para delimitar sentencias es extraña en MySQL, así que tienes que redefine el delimitador para poder crear procedimientos almacenados. No olvídese de cambiar el delimitador de nuevo!
  • INFORMATION_SCHEMA es global para todas las bases de datos, no se olvide de filtrar en TABLE_SCHEMA=DATABASE(). DATABASE() devuelve el nombre de la base de datos seleccionada actualmente.
 77
Author: geekQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-02 00:01:08

La mayoría de las respuestas abordan cómo agregar una columna de forma segura en un procedimiento almacenado, tuve la necesidad de agregar una columna a una tabla de forma segura sin usar un proc almacenado y descubrí que MySQL no permite el uso de IF Exists() fuera de un SP. Voy a publicar mi solución que podría ayudar a alguien en la misma situación.

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;
 31
Author: rahvin_t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-14 23:41:12

Si está en MariaDB, no es necesario utilizar procedimientos almacenados. Solo use, por ejemplo:

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

Ver aquí

 27
Author: giuseppe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-17 11:01:17

Otra forma de hacer esto sería ignorar el error con un 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();;

Creo que es más ordenado de esta manera que con una subconsulta exists. Especialmente si tiene muchas columnas que agregar y desea ejecutar el script varias veces.

Se puede encontrar más información sobre los controladores de continuar en http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html

 22
Author: Jake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-25 05:38:16

Estoy usando MySQL 5.5.19.

Me gusta tener scripts que puede ejecutar y volver a ejecutar sin error, especialmente donde las advertencias parecen persistir, apareciendo de nuevo más tarde mientras estoy ejecutando scripts que no tienen errores/advertencias. En cuanto a agregar campos, me escribí un procedimiento para hacerlo un poco menos escribiendo:

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

El código para crear el procedimiento addFieldIfNotExists es el siguiente:

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

No escribí un procedimiento para modificar de forma segura una columna, pero creo que el procedimiento anterior podría modificarse fácilmente para hacerlo.

 6
Author: Jonathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-21 21:03:40

He tomado el sproc de la OP y lo he hecho reutilizable e independiente del esquema. Obviamente todavía requiere 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 ;
 5
Author: Thomas Paine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-29 18:19:51

Acabo de probar el script de procedimiento almacenado. Parece que el problema son las marcas ' alrededor de los delimitadores. Los documentos de MySQL muestran que los caracteres delimitadores no necesitan comillas simples.

Así que quieres:

delimiter //

En lugar de:

delimiter '//'

Funciona para mí:)

 1
Author: Boy Who Roared,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-08 20:38:32

Si está ejecutando esto en un script, querrá agregar la siguiente línea después para volver a ejecutarlo, de lo contrario obtendrá un error procedure already exists.

drop procedure foo;
 1
Author: mat crocker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-08 12:40:26

La mejor manera de agregar la columna en PHP > PDO:

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

Nota: la columna en la tabla no es repetible, eso significa que no necesitamos verificar la existencia de una columna, pero para resolver el problema verificamos el código anterior:

Por ejemplo, si funciona alerta 1, si no 0, lo que significa que la columna existe ! :)

 1
Author: Maher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-15 21:59:15

Compruebe si la columna Existe o no en la DOP (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
Author: Maher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-16 15:48:08

Procedimiento de Jake https://stackoverflow.com/a/6476091/6751901 es una solución muy simple y buena para agregar nuevas columnas, pero con una línea adicional:

DROP PROCEDURE IF EXISTS foo;;

Puede agregar nuevas columnas más tarde allí, y también funcionará la próxima vez:

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();;
 1
Author: simpel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:08
$smpt = $pdo->prepare("SHOW fields FROM __TABLE__NAME__");
$smpt->execute();
$res = $smpt->fetchAll(PDO::FETCH_ASSOC);
//print_r($res);

Luego en res res por ciclo busca la clave de tu columna Me gusta esto:

    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;
    }
 0
Author: realmag777,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-06 20:41:31

A continuación se muestra el procedimiento almacenado en MySQL Para Agregar Columnas en diferentes Tablas en diferentes Bases de datos si la columna no existe en una(s) Tabla(s) de Bases de datos con las siguientes ventajas

  • se pueden agregar varias columnas use a la vez para alterar varias tablas en diferentes bases de datos
  • se ejecutan tres comandos mysql, es decir, DROP, CREATE, CALL For Procedure
  • Nombre de la BASE de DATOS debe ser cambios según el USO de lo contrario puede ocurrir un problema para múltiples datos

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();
 0
Author: Abdul Rehman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-13 08:33:59
ALTER TABLE `subscriber_surname` ADD  IF NOT EXISTS  `#__comm_subscribers`.`subscriber_surname`;

ALTER TABLE `#__comm_subscribers` MODIFY `subscriber_surname` varchar(64) NOT NULL default '';
 -1
Author: Phạm Trần Phú Quốc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-14 11:13:24