Utilizzo di ALTER per eliminare una colonna se esiste in MySQL


92

Come può essere utilizzato ALTER per eliminare una colonna in una tabella MySQL se quella colonna esiste?

So di poterlo usare ALTER TABLE my_table DROP COLUMN my_column, ma ciò genererà un errore se my_columnnon esiste. Esiste una sintassi alternativa per eliminare la colonna in modo condizionale?

Sto usando MySQL versione 4.0.18.


6
Questa domanda è stata menzionata su Meta .
Solo uno studente il

Risposte:


70

Per MySQL, non ce n'è nessuno: MySQL Feature Request .

Consentire ciò è probabilmente una pessima idea, comunque: IF EXISTSindica che stai eseguendo operazioni distruttive su un database con (per te) una struttura sconosciuta. Potrebbero esserci situazioni in cui questo è accettabile per un lavoro locale veloce e sporco, ma se sei tentato di eseguire una dichiarazione del genere contro i dati di produzione (in una migrazione ecc.), Stai giocando con il fuoco.

Ma se insisti, non è difficile semplicemente verificare l'esistenza prima nel client o rilevare l'errore.

MariaDB supporta anche quanto segue a partire dalla 10.0.2:

DROP [COLUMN] [IF EXISTS] col_name 

cioè

ALTER TABLE my_table DROP IF EXISTS my_column;

Ma è probabilmente una cattiva idea fare affidamento su una funzionalità non standard supportata da solo uno dei numerosi fork di MySQL.


8
C'è un modo per farlo in SQL puro?
Tom

16
Wow. Menzionato nel 2005 - 9 anni fa. Immagino che questo sia in fondo alla lista delle priorità ...
crmpicco

4
MariaDB lo supporta a partire dalla 10.0.2
Dfr

17
"Consentire questo è probabilmente una pessima idea", non sono d'accordo. Perché qualcuno dovrebbe fare supposizioni sui casi d'uso degli utenti? Ho un sacco di database e ho bisogno di sincronizzarli. È davvero fastidioso quando il software vuole essere più intelligente dell'essere umano ...
Onkeltem

5
14 anni dopo, ancora non lì. Non credo che questo verrà mai fatto.
Steve Horvath

45

Non c'è supporto a livello di lingua per questo in MySQL. Ecco una soluzione che coinvolge i metadati di MySQL information_schema in 5.0+, ma non risolverà il tuo problema nella 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;

Ho scritto alcune informazioni più dettagliate in un post sul blog .


3
Ho pensato che fosse importante riassumere il contributo di DrHyde come un commento, perché non è evidente quando è in una risposta propria. Assicurati di controllare che non stai modificando un database diverso: SELEZIONA * da information_schema.columns WHERE table_name = "country" AND column_name = "updated_at" AND table_schema = DATABASE () \ G
Homer6

Se non vuoi ricevere avvisi dalla "procedura di eliminazione se esiste schema_change;" aggiungi "set sql_notes = 0;" prima della prima riga e aggiungi "set sql_notes = 1;" dopo l'ultima riga. Dettagli -> stackoverflow.com/questions/27616564/suppress-mysql-warnings
csonuryilmaz

"delimiter" dovrebbe essere senza "" (ad es. -> delimiter ;;)
Illidan

17

So che questo è un vecchio thread, ma esiste un modo semplice per gestire questo requisito senza utilizzare stored procedure. Questo può aiutare qualcuno.

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 ;

Spero che questo aiuti qualcuno, come ha fatto a me (dopo molti tentativi ed errori).


Di sicuro lo ha fatto. Grazie @Pradeep
Sumit Deshmukh

14

Ho appena creato una procedura riutilizzabile che può aiutare a rendere DROP COLUMNidempotente:

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

Utilizzo:

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

Esempio:

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

5

La risposta di Chase Seibert funziona, ma aggiungerei che se hai diversi schemi vuoi modificare il SELECT in questo modo:

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

1

Forse il modo più semplice per risolvere questo problema (che funzionerà) è:

  • CREA new_table COME SELEZIONA id, col1, col2, ... (solo le colonne che vuoi effettivamente nella tabella finale) FROM my_table;

  • RENAME my_table TO old_table, new_table TO my_table;

  • DROP old_table;

Oppure mantieni old_table per un rollback, se necessario.

Funzionerà ma le chiavi esterne non verranno spostate. Dovresti aggiungerli di nuovo a my_table più tardi; anche le chiavi esterne in altre tabelle che fanno riferimento a my_table dovranno essere corrette (puntate al nuovo my_table).

In bocca al lupo...


1

È possibile utilizzare questo script, utilizzare la colonna, lo schema e il nome della tabella

 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;


-3

Mi rendo conto che questo thread è piuttosto vecchio ora, ma stavo avendo lo stesso problema. Questa era la mia soluzione di base utilizzando MySQL Workbench, ma ha funzionato bene ...

  1. ottieni un nuovo editor sql ed esegui SHOW TABLES per ottenere un elenco delle tue tabelle
  2. seleziona tutte le righe e scegli copia negli appunti (senza virgolette) dal menu contestuale
  3. incolla l'elenco dei nomi in un'altra scheda dell'editor
  4. scrivi la tua query, cioè ALTER TABLE xDROP a;
  5. copia e incolla, così finisci con una query separata per ogni tabella
  6. Decidi se il workbench deve arrestarsi quando si verifica un errore
  7. Premi esegui e guarda nel registro di output

tutte le tabelle che avevano la tabella ora non hanno tabelle che non hanno mostrato un errore nei log

quindi puoi trovare / sostituire 'drop a' cambiarlo in 'ADD COLUMN bINT NULL' ecc. ed eseguire di nuovo il tutto ...

un po 'goffo, ma alla fine ottieni il risultato finale e puoi controllare / monitorare l'intero processo e ricordarti di salvare gli script sql nel caso ne avessi bisogno di nuovo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.