web-dev-qa-db-fra.com

Utiliser ALTER pour supprimer une colonne si elle existe dans MySQL

Comment ALTER peut-il être utilisé pour supprimer une colonne dans une table MySQL si cette colonne existe?

Je sais que je peux utiliser ALTER TABLE my_table DROP COLUMN my_column, mais cela générera une erreur si my_column n'existe pas. Existe-t-il une syntaxe alternative pour supprimer la colonne de manière conditionnelle?

J'utilise MySQL version 4.0.18.

71
jcodeninja

Pour MySQL, il n'y en a pas: MySQL Feature Request .

Permettre cela est sans doute une très mauvaise idée, de toute façon: IF EXISTS indique que vous exécutez des opérations destructrices sur une base de données avec (pour vous) une structure inconnue. Il peut y avoir des situations où cela est acceptable pour un travail local rapide et sale, mais si vous êtes tenté d'exécuter une telle déclaration par rapport aux données de production (dans une migration, etc.), vous jouez avec le feu.

Mais si vous insistez, il n'est pas difficile de vérifier d'abord l'existence du client ou de détecter l'erreur.

MariaDB prend également en charge les éléments suivants à partir de 10.0.2:

DROP [COLUMN] [IF EXISTS] col_name

je. e.

ALTER TABLE my_table DROP IF EXISTS my_column;

Mais c'est sans doute une mauvaise idée de s'appuyer sur une fonctionnalité non standard prise en charge par une seule des nombreuses fourches de MySQL.

53
MattW.

Il n'y a pas de support de niveau de langue pour cela dans MySQL. Voici une solution de contournement impliquant les métadonnées MySQL information_schema dans la version 5.0+, mais elle ne résoudra pas votre problème dans la version 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;

J'ai écrit des informations plus détaillées dans un article de blog .

42
Chase Seibert

Je sais que c'est un vieux fil, mais il existe un moyen simple de gérer cette exigence sans utiliser de procédures stockées. Cela peut aider quelqu'un.

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 ;

J'espère que cela aide quelqu'un, tout comme moi (après beaucoup d'essais et d'erreurs).

11
Pradeep Puranik

Je viens de créer une procédure réutilisable qui peut aider à faire DROP COLUMN idempotent:

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

Usage:

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

Exemple:

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
10
sp00m

La réponse de Chase Seibert fonctionne, mais j'ajouterais que si vous avez plusieurs schémas, vous voulez modifier le SELECT ainsi:

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

Le moyen le plus simple de résoudre ce problème (qui fonctionnera) est peut-être:

  • CREATE new_table AS SELECT id, col1, col2, ... (uniquement les colonnes que vous voulez réellement dans la table finale) FROM my_table;

  • RENAME my_table TO old_table, new_table TO my_table;

  • DROP old_table;

Ou conservez old_table pour une restauration si nécessaire.

Cela fonctionnera mais les clés étrangères ne seront pas déplacées. Vous devrez les rajouter à my_table plus tard; les clés étrangères dans d'autres tables qui référencent my_table devront être corrigées (pointées vers la nouvelle my_table).

Bonne chance...

0
Frank Flynn