Usando ALTER para eliminar uma coluna se ela existir no MySQL

94

Como ALTER pode ser usado para eliminar uma coluna em uma tabela MySQL se essa coluna existir?

Sei que posso usar ALTER TABLE my_table DROP COLUMN my_column, mas isso vai dar um erro se my_columnnão existir. Existe uma sintaxe alternativa para descartar a coluna condicionalmente?

Estou usando o MySQL versão 4.0.18.

jcodeninja
fonte
6
Esta questão foi mencionada no Meta .
Apenas um estudante de

Respostas:

73

Para MySQL, não há nenhum: Solicitação de recurso MySQL .

Permitir isso é sem dúvida uma ideia muito ruim, de qualquer maneira: IF EXISTSindica que você está executando operações destrutivas em um banco de dados com (para você) estrutura desconhecida. Pode haver situações em que isso seja aceitável para trabalho local rápido e sujo, mas se você estiver tentado a executar tal instrução em dados de produção (em uma migração etc.), você está brincando com fogo.

Mas se você insiste, não é difícil simplesmente verificar a existência primeiro no cliente ou detectar o erro.

MariaDB também suporta o seguinte, começando com 10.0.2:

DROP [COLUMN] [IF EXISTS] col_name 

ie

ALTER TABLE my_table DROP IF EXISTS my_column;

Mas é indiscutivelmente uma má ideia confiar em um recurso não padrão suportado por apenas um dos vários fork do MySQL.

Matthias Winkelmann
fonte
8
Existe uma maneira de fazer isso em SQL puro?
Tom
16
Uau. Mencionado em 2005 - 9 anos atrás. Estou supondo que isso está no fim da lista de prioridades ...
crmpicco
4
MariaDB oferece suporte a partir de 10.0.2
Dfr
20
“Permitir isso é sem dúvida uma péssima ideia,” - Eu não concordo. Por que alguém deveria fazer suposições sobre os casos de uso dos usuários? Tenho vários bancos de dados e preciso sincronizá-los. É realmente irritante quando o software quer ser mais inteligente do que o humano ...
Onkeltem
5
14 anos depois, ainda não lá. Eu não acho que isso jamais será feito.
Steve Horvath
45

Não há suporte de nível de idioma para isso no MySQL. Aqui está uma solução alternativa envolvendo metadados information_schema do MySQL no 5.0+, mas não resolverá o seu problema no 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;

Escrevi algumas informações mais detalhadas em uma postagem do blog .

Chase Seibert
fonte
3
Achei importante resumir a contribuição do DrHyde como um comentário, porque não fica aparente quando é uma resposta própria. Certifique-se de verificar se você não está modificando um banco de dados diferente: SELECT * from information_schema.columns WHERE table_name = "país" AND column_name = "updated_at" AND table_schema = DATABASE () \ G
Homer6
Se você não quiser receber avisos de "drop procedure if existing schema_change;" adicione "definir sql_notes = 0;" antes da primeira linha e adicione "set sql_notes = 1;" após a última linha. Detalhes -> stackoverflow.com/questions/27616564/suppress-mysql-warnings
csonuryilmaz
"delimitador" deve estar sem '' (por exemplo -> delimitador ;;)
Illidan
17

Eu sei que este é um segmento antigo, mas há uma maneira simples de lidar com esse requisito sem usar procedimentos armazenados. Isso pode ajudar alguém.

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 isso ajude alguém, assim como a mim (depois de muitas tentativas e erros).

Pradeep Puranik
fonte
Claro que sim. Obrigado @Pradeep
Sumit Deshmukh
14

Acabei de construir um procedimento reutilizável que pode ajudar a tornar 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 ;

Uso:

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

Exemplo:

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
fonte
5

A resposta de Chase Seibert funciona, mas eu acrescentaria que, se você tiver vários esquemas, deseja alterar o SELECT assim:

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

Talvez a maneira mais simples de resolver isso (que funcione) seja:

  • CREATE new_table AS SELECT id, col1, col2, ... (somente as colunas que você realmente deseja na tabela final) FROM my_table;

  • RENOMEAR my_table TO old_table, new_table TO my_table;

  • DROP old_table;

Ou mantenha old_table para uma reversão, se necessário.

Isso funcionará, mas as chaves estrangeiras não serão movidas. Você teria que adicioná-los novamente a minha_tabela mais tarde; também as chaves estrangeiras em outras tabelas que fazem referência a minha_tabela terão que ser corrigidas (apontadas para a nova minha_tabela).

Boa sorte...

Frank Flynn
fonte
1

Você pode usar este script, usar sua coluna, esquema e nome de tabela

 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ƞ
fonte
-3

Sei que esse tópico está bem antigo agora, mas eu estava tendo o mesmo problema. Esta era minha solução básica usando o MySQL Workbench, mas funcionou bem ...

  1. obtenha um novo editor sql e execute SHOW TABLES para obter uma lista de suas tabelas
  2. selecione todas as linhas e escolha copiar para a área de transferência (sem aspas) no menu de contexto
  3. cole a lista de nomes em outra guia do editor
  4. escreva sua consulta, isto é, ALTER TABLE xDROP a;
  5. copie e cole, então você acaba com uma consulta separada para cada tabela
  6. Alternar se o ambiente de trabalho deve parar quando ocorre um erro
  7. Clique em executar e examine o log de saída

quaisquer tabelas que tinham a tabela agora não têm nenhuma tabela que não tivesse mostrado um erro nos logs

então você pode encontrar / substituir 'drop a' alterá-lo para 'ADD COLUMN bINT NULL' etc e executar tudo novamente ....

um pouco desajeitado, mas finalmente você obtém o resultado final e pode controlar / monitorar todo o processo e lembrar de salvar seus scripts sql caso precise deles novamente.

ajp
fonte