Utiliser ALTER pour supprimer une colonne si elle existe dans MySQL

92

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 jettera une erreur s'il my_columnn'existe pas. Existe-t-il une autre syntaxe pour supprimer la colonne de manière conditionnelle?

J'utilise MySQL version 4.0.18.

jcodeninja
la source
6
Cette question a été mentionnée sur Meta .
Juste un étudiant

Réponses:

70

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

Autoriser cela est sans doute une très mauvaise idée, de toute façon: IF EXISTSindique que vous exécutez des opérations destructives 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 contre des données de production (dans une migration, etc.), vous jouez avec le feu.

Mais si vous insistez, il n'est pas difficile de simplement vérifier d'abord l'existence dans le client, ou d'attraper l'erreur.

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

DROP [COLUMN] [IF EXISTS] col_name 

c'est à dire

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 supportée par une seule des nombreuses fourches de MySQL.

Matthias Winkelmann
la source
8
Existe-t-il un moyen de le faire en SQL pur?
Tom le
16
Sensationnel. Mentionné en 2005 - il y a 9 ans. Je suppose que c'est en bas de la liste des priorités ...
crmpicco
4
MariaDB le prend en charge à partir de 10.0.2
Dfr
17
"Permettre cela est sans doute une très mauvaise idée," - Je ne suis pas d'accord. Pourquoi quelqu'un devrait-il faire des hypothèses sur les cas d'utilisation des utilisateurs? J'ai un tas de bases de données et je dois les synchroniser. C'est vraiment ennuyeux quand un logiciel veut être plus intelligent qu'humain ...
Onkeltem
5
14 ans plus tard, toujours pas là. Je ne pense pas que cela se fera jamais.
Steve Horvath
45

Il n'y a pas de support de niveau de langue pour cela dans MySQL. Voici une solution de contournement impliquant les méta-donné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 .

Chase Seibert
la source
3
J'ai pensé qu'il était important de résumer la contribution de DrHyde sous forme de commentaire, car ce n'est pas apparent quand il s'agit d'une réponse qui lui est propre. Assurez-vous de vérifier que vous ne modifiez pas une base de données différente: SELECT * from information_schema.columns WHERE table_name = "country" AND column_name = "updated_at" AND table_schema = DATABASE () \ G
Homer6
Si vous ne voulez pas recevoir d'avertissements de la "procédure de suppression s'il existe schema_change;" ajouter "set sql_notes = 0;" avant la première ligne et ajoutez "set sql_notes = 1;" après la dernière ligne. Détails -> stackoverflow.com/questions/27616564/suppress-mysql-warnings
csonuryilmaz
"delimiter" devrait être sans '' (par exemple -> delimiter ;;)
Illidan
17

Je sais qu'il s'agit d'un ancien thread, 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, comme moi (après de nombreux essais et erreurs).

Pradeep Puranik
la source
Ça l'a fait. Merci @Pradeep
Sumit Deshmukh
14

Je viens de créer une procédure réutilisable qui peut aider à rendre DROP COLUMNidempotent:

-- 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
sp00m
la source
5

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

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

La façon la plus simple de résoudre ce problème (qui fonctionnera) est peut-être:

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

  • RENOMMER ma_table à ancienne_table, nouvelle_table à ma_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 plus tard à ma_table; également 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...

Frank Flynn
la source
1

Vous pouvez utiliser ce script, utiliser votre colonne, votre schéma et le nom de votre table

 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ƞ
la source
-3

Je me rends compte que ce fil est assez vieux maintenant, mais j'avais le même problème. C'était ma solution très basique utilisant MySQL Workbench, mais cela a bien fonctionné ...

  1. obtenir un nouvel éditeur sql et exécuter SHOW TABLES pour obtenir une liste de vos tables
  2. sélectionnez toutes les lignes et choisissez Copier dans le presse-papiers (sans guillemets) dans le menu contextuel
  3. collez la liste des noms dans un autre onglet de l'éditeur
  4. écrivez votre requête, c'est-à-dire ALTER TABLE xDROP a;
  5. faire un peu de copier-coller, de sorte que vous vous retrouvez avec une requête distincte pour chaque table
  6. Indique si l'atelier doit s'arrêter lorsqu'une erreur se produit
  7. Appuyez sur exécuter et parcourez le journal de sortie

toutes les tables qui avaient la table n'ont maintenant aucune table qui n'a pas montré une erreur dans les journaux

alors vous pouvez trouver / remplacer 'drop a' changez-le en 'ADD COLUMN bINT NULL' etc et relancez le tout ....

un peu maladroit, mais enfin vous obtenez le résultat final et vous pouvez contrôler / surveiller l'ensemble du processus et n'oubliez pas de sauvegarder vos scripts SQL au cas où vous en auriez à nouveau besoin.

ajp
la source