Użycie ALTER do usunięcia kolumny, jeśli istnieje w MySQL

92

W jaki sposób można użyć ALTER do usunięcia kolumny w tabeli MySQL, jeśli taka kolumna istnieje?

Wiem, że mogę użyć ALTER TABLE my_table DROP COLUMN my_column, ale jeśli my_columnnie istnieje , spowoduje to wyświetlenie błędu . Czy istnieje alternatywna składnia warunkowego usuwania kolumny?

Używam MySQL w wersji 4.0.18.

jcodeninja
źródło
6
To pytanie zostało wspomniane na Meta .
Tylko student

Odpowiedzi:

70

W przypadku MySQL nie ma żadnego: Żądanie funkcji MySQL .

W każdym razie pozwolenie na to jest prawdopodobnie bardzo złym pomysłem: IF EXISTSwskazuje, że wykonujesz destrukcyjne operacje na bazie danych o (dla Ciebie) nieznanej strukturze. Mogą zaistnieć sytuacje, w których jest to dopuszczalne w przypadku szybkiej i brudnej pracy lokalnej, ale jeśli masz pokusę, aby uruchomić takie oświadczenie na danych produkcyjnych (podczas migracji itp.), Bawisz się ogniem.

Ale jeśli nalegasz, nie jest trudno po prostu najpierw sprawdzić istnienie w kliencie lub wyłapać błąd.

MariaDB obsługuje również następujące elementy począwszy od 10.0.2:

DROP [COLUMN] [IF EXISTS] col_name 

to znaczy

ALTER TABLE my_table DROP IF EXISTS my_column;

Ale zapewne złym pomysłem jest poleganie na niestandardowej funkcji obsługiwanej tylko przez jedną z kilku rozwidleń MySQL.

Matthias Winkelmann
źródło
8
Czy można to zrobić w czystym SQL?
Tom
16
Łał. Wzmiankowany w 2005 - 9 lat temu. Domyślam się, że to jest na dole listy priorytetów ...
crmpicco
4
MariaDB obsługuje go od 10.0.2
Dfr
17
„Dopuszczenie do tego jest prawdopodobnie naprawdę złym pomysłem” - nie zgadzam się. Dlaczego ktoś miałby robić założenia dotyczące przypadków użycia przez użytkowników? Mam kilka baz danych i muszę je zsynchronizować. To naprawdę denerwujące, gdy oprogramowanie chce być mądrzejsze od człowieka ...
Onkeltem
5
14 lat później, nadal nie ma. Nie sądzę, żeby to kiedykolwiek się udało.
Steve Horvath
45

MySQL nie obsługuje tego na poziomie języka. Oto obejście obejmujące metadane MySQL information_schema w wersji 5.0+, ale nie rozwiąże problemu w wersji 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;

Bardziej szczegółowe informacje napisałem w poście na blogu .

Ścigaj Seiberta
źródło
3
Pomyślałem, że ważne jest podsumowanie wkładu DrHyde w komentarzu, ponieważ nie jest to oczywiste, gdy jest to odpowiedź własna. Upewnij się, że nie modyfikujesz innej bazy danych: SELECT * from information_schema.columns WHERE table_name = "country" AND column_name = "updated_at" AND table_schema = DATABASE () \ G
Homer6
Jeśli nie chcesz otrzymywać ostrzeżeń z „procedury drop, jeśli istnieje schema_change;” add "set sql_notes = 0;" przed pierwszą linią i dodaj „set sql_notes = 1;” po ostatniej linii. Szczegóły -> stackoverflow.com/questions/27616564/suppress-mysql-warnings
csonuryilmaz
„delimiter” powinno być bez „” (np. -> delimiter ;;)
Illidan
17

Wiem, że to stary wątek, ale istnieje prosty sposób obsługi tego wymagania bez korzystania z procedur składowanych. To może komuś pomóc.

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 ;

Mam nadzieję, że to komuś pomoże, tak jak mi (po wielu próbach i błędach).

Pradeep Puranik
źródło
Z pewnością tak. Dzięki @Pradeep
Sumit Deshmukh
14

Właśnie zbudowałem procedurę wielokrotnego użytku, która może pomóc w stworzeniu DROP COLUMNidempotencji:

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

Stosowanie:

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

Przykład:

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
źródło
5

Odpowiedź Chase Seiberta działa, ale dodam, że jeśli masz kilka schematów, chcesz zmienić SELECT w ten sposób:

select * from information_schema.columns where table_schema in (select schema()) and table_name=...
DrHyde
źródło
1

Być może najprostszym sposobem rozwiązania tego problemu (który zadziała) jest:

  • UTWÓRZ nową_tabelę AS SELECT id, kol1, kol2, ... (tylko kolumny, które chcesz umieścić w ostatecznej tabeli) FROM my_table;

  • RENAME my_table TO old_table, new_table TO my_table;

  • DROP old_table;

Lub zachowaj old_table do wycofania w razie potrzeby.

To zadziała, ale klucze obce nie zostaną przeniesione. Będziesz musiał później ponownie dodać je do my_table; również klucze obce w innych tabelach, które odwołują się do my_table, będą musiały zostać naprawione (wskazano na nową my_table).

Powodzenia...

Frank Flynn
źródło
1

Możesz użyć tego skryptu, użyć swojej kolumny, schematu i nazwy tabeli

 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ƞ
źródło
-3

Zdaję sobie sprawę, że ten wątek jest teraz dość stary, ale miałem ten sam problem. To było moje bardzo podstawowe rozwiązanie wykorzystujące MySQL Workbench, ale działało dobrze ...

  1. pobierz nowy edytor sql i wykonaj SHOW TABLES, aby uzyskać listę swoich tabel
  2. zaznacz wszystkie wiersze i wybierz Kopiuj do schowka (bez cudzysłowów) z menu kontekstowego
  3. wklej listę nazwisk do innej zakładki edytora
  4. napisz zapytanie, np. ALTER TABLE xDROP a;
  5. wykonaj pewne kopiowanie i wklejanie, aby otrzymać oddzielne zapytanie dla każdej tabeli
  6. Przełącz, czy środowisko robocze ma się zatrzymać po wystąpieniu błędu
  7. Naciśnij przycisk wykonaj i przejrzyj dziennik wyjściowy

wszystkie tabele, które miały tabelę, nie mają teraz żadnych tabel, które nie mają, wyświetlą błąd w dziennikach

wtedy możesz znaleźć / zamienić „upuść a”, zmienić na „DODAJ KOLUMNĘb INT NULL” itp. i ponownie uruchomić całość ....

trochę niezgrabne, ale w końcu otrzymujesz wynik końcowy i możesz kontrolować / monitorować cały proces i pamiętać o zapisywaniu skryptów sql na wypadek, gdybyś ich ponownie potrzebował.

ajp
źródło