Usando ALTER para eliminar una columna si existe en MySQL

92

¿Cómo se puede usar ALTER para colocar una columna en una tabla MySQL si esa columna existe?

Sé que puedo usarlo ALTER TABLE my_table DROP COLUMN my_column, pero eso arrojará un error si my_columnno existe. ¿Existe una sintaxis alternativa para descartar la columna de forma condicional?

Estoy usando MySQL versión 4.0.18.

jcodeninja
fuente
6
Esta pregunta se mencionó en Meta .
Solo un estudiante

Respuestas:

70

Para MySQL, no hay ninguno: MySQL Feature Request .

Permitir esto podría decirse que es una muy mala idea, de todos modos: IF EXISTSindica que está ejecutando operaciones destructivas en una base de datos con (para usted) una estructura desconocida. Puede haber situaciones en las que esto sea aceptable para un trabajo local rápido y sucio, pero si tiene la tentación de ejecutar una declaración de este tipo contra los datos de producción (en una migración, etc.), está jugando con fuego.

Pero si insiste, no es difícil simplemente verificar la existencia primero en el cliente o detectar el error.

MariaDB también admite lo siguiente a partir de 10.0.2:

DROP [COLUMN] [IF EXISTS] col_name 

es decir

ALTER TABLE my_table DROP IF EXISTS my_column;

Pero podría decirse que es una mala idea confiar en una función no estándar compatible con solo una de las varias bifurcaciones de MySQL.

Matthias Winkelmann
fuente
8
¿Hay alguna forma de hacerlo en SQL puro?
Tom
16
Guau. Mencionado en 2005 - hace 9 años. Supongo que esto está en la lista de prioridades ...
crmpicco
4
MariaDB lo admite a partir de 10.0.2
Dfr
17
"Permitir esto es posiblemente una muy mala idea", no estoy de acuerdo. ¿Por qué alguien debería hacer suposiciones sobre los casos de uso de los usuarios? Tengo un montón de bases de datos y necesito sincronizarlas. Es realmente molesto cuando el software quiere ser más inteligente que los humanos ...
Onkeltem
5
14 años después, todavía no está. No creo que esto se haga nunca.
Steve Horvath
45

No hay soporte de nivel de idioma para esto en MySQL. Aquí hay una solución que involucra a los metadatos de MySQL information_schema en 5.0+, pero no abordará su problema en 4.0.18.

drop procedure if exists schema_change;

delimiter ';;'
create procedure schema_change() begin

    /* delete columns if they exist */
    if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column1') then
        alter table table1 drop column `column1`;
    end if;
    if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column2') then
        alter table table1 drop column `column2`;
    end if;

    /* add columns */
    alter table table1 add column `column1` varchar(255) NULL;
    alter table table1 add column `column2` varchar(255) NULL;

end;;

delimiter ';'
call schema_change();

drop procedure if exists schema_change;

Escribí información más detallada en una publicación de blog .

Chase Seibert
fuente
3
Pensé que era importante resumir la contribución de DrHyde como un comentario, porque no es evidente cuando está en una respuesta propia. Asegúrese de verificar que no está modificando una base de datos diferente: SELECT * from information_schema.columns WHERE table_name = "country" AND column_name = "updated_at" AND table_schema = DATABASE () \ G
Homer6
Si no desea recibir advertencias de "eliminar procedimiento si existe schema_change;" agregar "set sql_notes = 0;" antes de la primera línea y agregue "set sql_notes = 1;" después de la última línea. Detalles -> stackoverflow.com/questions/27616564/suppress-mysql-warnings
csonuryilmaz
"delimitador" debe estar sin '' (p. ej. -> delimitador ;;)
Illidan
17

Sé que este es un hilo antiguo, pero hay una forma sencilla de manejar este requisito sin utilizar procedimientos almacenados. Esto puede ayudar a alguien.

set @exist_Check := (
    select count(*) from information_schema.columns 
    where TABLE_NAME='YOUR_TABLE' 
    and COLUMN_NAME='YOUR_COLUMN' 
    and TABLE_SCHEMA=database()
) ;
set @sqlstmt := if(@exist_Check>0,'alter table YOUR_TABLE drop column YOUR_COLUMN', 'select ''''') ;
prepare stmt from @sqlstmt ;
execute stmt ;

Espero que esto ayude a alguien, como lo hizo a mí (después de mucho ensayo y error).

Pradeep Puranik
fuente
Seguro que lo hizo. Gracias @Pradeep
Sumit Deshmukh
14

Acabo de construir un procedimiento reutilizable que puede ayudar a DROP COLUMNvolverse idempotente:

-- column_exists:

DROP FUNCTION IF EXISTS column_exists;

DELIMITER $$
CREATE FUNCTION column_exists(
  tname VARCHAR(64),
  cname VARCHAR(64)
)
  RETURNS BOOLEAN
  READS SQL DATA
  BEGIN
    RETURN 0 < (SELECT COUNT(*)
                FROM `INFORMATION_SCHEMA`.`COLUMNS`
                WHERE `TABLE_SCHEMA` = SCHEMA()
                      AND `TABLE_NAME` = tname
                      AND `COLUMN_NAME` = cname);
  END $$
DELIMITER ;

-- drop_column_if_exists:

DROP PROCEDURE IF EXISTS drop_column_if_exists;

DELIMITER $$
CREATE PROCEDURE drop_column_if_exists(
  tname VARCHAR(64),
  cname VARCHAR(64)
)
  BEGIN
    IF column_exists(tname, cname)
    THEN
      SET @drop_column_if_exists = CONCAT('ALTER TABLE `', tname, '` DROP COLUMN `', cname, '`');
      PREPARE drop_query FROM @drop_column_if_exists;
      EXECUTE drop_query;
    END IF;
  END $$
DELIMITER ;

Uso:

CALL drop_column_if_exists('my_table', 'my_column');

Ejemplo:

SELECT column_exists('my_table', 'my_column');       -- 1
CALL drop_column_if_exists('my_table', 'my_column'); -- success
SELECT column_exists('my_table', 'my_column');       -- 0
CALL drop_column_if_exists('my_table', 'my_column'); -- success
SELECT column_exists('my_table', 'my_column');       -- 0
sp00m
fuente
5

La respuesta de Chase Seibert funciona, pero agregaría que si tiene varios esquemas, desea alterar SELECT de esta manera:

select * from information_schema.columns where table_schema in (select schema()) and table_name=...
DrHyde
fuente
1

Quizás la forma más sencilla de resolver esto (que funcionará) es:

  • CREAR new_table AS SELECT id, col1, col2, ... (solo las columnas que realmente desea en la tabla final) FROM my_table;

  • CAMBIAR EL NOMBRE my_table TO old_table, new_table TO my_table;

  • DROP old_table;

O mantenga old_table para una reversión si es necesario.

Esto funcionará, pero las claves externas no se moverán. Tendría que volver a agregarlos a my_table más tarde; también las claves externas en otras tablas que hacen referencia a my_table tendrán que ser arregladas (señaladas a la nueva my_table).

Buena suerte...

Frank Flynn
fuente
1

Puede usar este script, usar su columna, esquema y nombre de tabla

 IF EXISTS (SELECT *
                         FROM INFORMATION_SCHEMA.COLUMNS
                         WHERE TABLE_NAME = 'TableName' AND COLUMN_NAME = 'ColumnName' 
                                             AND TABLE_SCHEMA = SchemaName)
    BEGIN
       ALTER TABLE TableName DROP COLUMN ColumnName;
    END;
Shah Zaiƞ
fuente
-3

Me doy cuenta de que este hilo es bastante antiguo ahora, pero yo estaba teniendo el mismo problema. Esta fue mi solución muy básica usando MySQL Workbench, pero funcionó bien ...

  1. obtenga un nuevo editor sql y ejecute SHOW TABLES para obtener una lista de sus tablas
  2. seleccione todas las filas y elija copiar al portapapeles (sin comillas) en el menú contextual
  3. pegar la lista de nombres en otra pestaña del editor
  4. escriba su consulta, es decir, ALTER TABLE xDROPa ;
  5. hacer algunas copias y pegar, por lo que terminará con una consulta separada para cada tabla
  6. Alternar si el banco de trabajo debe detenerse cuando ocurre un error
  7. Pulsa ejecutar y mira el registro de salida

cualquier tabla que tenía la tabla ahora no tiene ninguna tabla que no haya mostrado un error en los registros

luego puede buscar / reemplazar 'soltar a' cambiarlo a 'AGREGAR COLUMNAb INT NULL', etc. y ejecutar todo nuevamente ...

un poco torpe, pero al final obtiene el resultado final y puede controlar / monitorear todo el proceso y recordar guardar sus scripts sql en caso de que los necesite nuevamente.

ajp
fuente