Fügen Sie der MySQL-Tabelle eine Spalte hinzu, falls diese nicht vorhanden ist


109

Meine Forschungen und Experimente haben noch keine Antwort geliefert, daher hoffe ich auf Hilfe.

Ich ändere die Installationsdatei einer Anwendung, die in früheren Versionen keine Spalte hatte, die ich jetzt hinzufügen möchte. Ich möchte die Spalte nicht manuell hinzufügen, sondern in der Installationsdatei und nur, wenn die neue Spalte noch nicht in der Tabelle vorhanden ist.

Die Tabelle wird wie folgt erstellt:

CREATE TABLE IF NOT EXISTS `#__comm_subscribers` (
      `subscriber_id` int(11) NOT NULL auto_increment,
      `user_id` int(11) NOT NULL default '0',
      `subscriber_name` varchar(64) NOT NULL default '',
      `subscriber_surname` varchar(64) NOT NULL default '',
      `subscriber_email` varchar(64) NOT NULL default '',
      `confirmed` tinyint(1) NOT NULL default '0',
      `subscribe_date` datetime NOT NULL default '0000-00-00 00:00:00',
      PRIMARY KEY  (`subscriber_id`),
      UNIQUE KEY `subscriber_email` (`subscriber_email`)
    ) ENGINE=MyISAM CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' COMMENT='Subscribers for Comm are stored here.';

Wenn ich unter der Anweisung create table Folgendes hinzufüge, bin ich mir nicht sicher, was passiert, wenn die Spalte bereits vorhanden ist (und möglicherweise ausgefüllt ist):

ALTER TABLE `#__comm_subscribers` ADD `subscriber_surname`;
ALTER TABLE `#__comm_subscribers` MODIFY `subscriber_surname` varchar(64) NOT NULL default '';

Also habe ich Folgendes versucht, was ich irgendwo gefunden habe. Dies scheint nicht zu funktionieren, aber ich bin nicht ganz sicher, ob ich es richtig verwendet habe.

/*delimiter '//'
CREATE PROCEDURE addcol() BEGIN
IF NOT EXISTS(
SELECT * FROM information_schema.COLUMNS
WHERE COLUMN_NAME='subscriber_surname' AND TABLE_NAME='#__comm_subscribers'
)
THEN
    ALTER TABLE `#__comm_subscribers`
    ADD COLUMN `subscriber_surname` varchar(64) NOT NULL default '';
END IF;
END;
//
delimiter ';'
CALL addcol();
DROP PROCEDURE addcol;*/

Hat jemand einen guten Weg, dies zu tun?


2
Das Ändern von information_schema.COLUMNS, dh was die gespeicherte Prozedur tut, ist meiner Meinung nach der richtige Weg. Welcher Teil davon "scheint nicht zu funktionieren"?
Rodion

Antworten:


49

Beachten Sie, dass INFORMATION_SCHEMAMySQL vor 5.0 nicht unterstützt wird. Gespeicherte Prozeduren werden auch nicht vor 5.0 unterstützt. Wenn Sie also MySQL 4.1 unterstützen müssen, ist diese Lösung nicht gut.

Eine Lösung, die von Frameworks verwendet wird, die Datenbankmigrationen verwenden, besteht darin, eine Versionsnummer für das Schema in Ihrer Datenbank aufzuzeichnen. Nur eine Tabelle mit einer einzelnen Spalte und einer einzelnen Zeile, wobei eine Ganzzahl angibt, welche Revision aktuell ist. Wenn Sie das Schema aktualisieren, erhöhen Sie die Anzahl.

Eine andere Lösung wäre, einfach den Befehl zu versuchenALTER TABLE ADD COLUMN . Es sollte ein Fehler ausgegeben werden, wenn die Spalte bereits vorhanden ist.

ERROR 1060 (42S21): Duplicate column name 'newcolumnname'

Fangen Sie den Fehler ab und ignorieren Sie ihn in Ihrem Upgrade-Skript.


1
OK, das ist wirklich grob, aber jemand muss es sagen. Wenn Sie einfach ein SQL-Skript über die Befehlszeile ausführen, können Sie mysql den --forceSchalter geben, was bedeutet, dass Sie auch dann weitermachen, wenn ein Fehler auftritt. dann mach es einfach. Sie möchten nur sichergehen, dass es keine Anweisungen gibt, die Sie NICHT erfolgreich sein möchten, wenn etwas Vorheriges fehlgeschlagen ist.
David

84

Hier ist eine funktionierende Lösung (gerade mit MySQL 5.0 unter Solaris ausprobiert):

DELIMITER $$

DROP PROCEDURE IF EXISTS upgrade_database_1_0_to_2_0 $$
CREATE PROCEDURE upgrade_database_1_0_to_2_0()
BEGIN

-- rename a table safely
IF NOT EXISTS( (SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE()
        AND TABLE_NAME='my_old_table_name') ) THEN
    RENAME TABLE 
        my_old_table_name TO my_new_table_name,
END IF;

-- add a column safely
IF NOT EXISTS( (SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE()
        AND COLUMN_NAME='my_additional_column' AND TABLE_NAME='my_table_name') ) THEN
    ALTER TABLE my_table_name ADD my_additional_column varchar(2048) NOT NULL DEFAULT '';
END IF;

END $$

CALL upgrade_database_1_0_to_2_0() $$

DELIMITER ;

Auf den ersten Blick sieht es wahrscheinlich komplizierter aus als es sollte, aber wir müssen uns hier mit folgenden Problemen befassen:

  • IF Anweisungen funktionieren nur in gespeicherten Prozeduren, nicht wenn sie direkt ausgeführt werden, z. B. im MySQL-Client
  • eleganter und prägnanter SHOW COLUMNSfunktioniert nicht in gespeicherten Prozeduren, daher muss INFORMATION_SCHEMA verwendet werden
  • Die Syntax zum Trennen von Anweisungen ist in MySQL seltsam, daher müssen Sie das Trennzeichen neu definieren, um gespeicherte Prozeduren erstellen zu können. Vergessen Sie nicht, das Trennzeichen zurückzuschalten!
  • INFORMATION_SCHEMA ist global für alle Datenbanken. Vergessen Sie nicht, nach zu filtern TABLE_SCHEMA=DATABASE(). DATABASE()Gibt den Namen der aktuell ausgewählten Datenbank zurück.

1
Ich wünschte, ich könnte Bonuspunkte für die Erklärung der Probleme vergeben, die zu diesem Ansatz geführt haben. Danke dir.
Bryan Petty

48

Wenn Sie sich in MariaDB befinden, müssen Sie keine gespeicherten Prozeduren verwenden. Verwenden Sie zum Beispiel:

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name tinyint(1) DEFAULT 0;

Siehe hier


8
Brillant! Ein weiterer Grund, MariaDB zu verwenden.
Andrew Ensley

+1 Ich habe die ganze Zeit mit Maria gearbeitet und all diese Schritte ausprobiert. Keiner von ihnen hat funktioniert, bis ich diesen erreicht habe. Das hat mir das Leben gerettet.
Thielicious

Interessant .. Ich habe nie viel über MariaDB und dieser Artikel kann eine Vorstellung davon geben seravo.fi/2015/…
walv

Diese Lösung ist großartig, erfordert jedoch MariaDB 10.0.2. Nur Heads-up für alle, die diese elegante Lösung nutzen möchten, sich aber auf eine ältere Version beschränken.
jblopez

ALTER TABLE Tabellenname CHANGE COLUMN IF EXISTS column_wrong_name Spaltenname tinyint (1) DEFAULT 0; -funktioniert auch gut!
Damonsson

34

Die meisten Antworten beziehen sich auf das sichere Hinzufügen einer Spalte in einer gespeicherten Prozedur. Ich musste einer Tabelle ohne Verwendung eines gespeicherten Prozesses sicher eine Spalte hinzufügen und stellte fest, dass MySQL die Verwendung IF Exists()außerhalb eines SP nicht zulässt . Ich werde meine Lösung veröffentlichen, damit sie jemandem in der gleichen Situation hilft.

SELECT count(*)
INTO @exist
FROM information_schema.columns 
WHERE table_schema = database()
and COLUMN_NAME = 'original_data'
AND table_name = 'mytable';

set @query = IF(@exist <= 0, 'alter table intent add column mycolumn4 varchar(2048) NULL after mycolumn3', 
'select \'Column Exists\' status');

prepare stmt from @query;

EXECUTE stmt;

1
Hinweis Ich musste der SELECT-Anweisung ein "LIMIT 1" hinzufügen, wenn ich über die MySQL-Workbench-GUI arbeitete.
Al Dass

23

Eine andere Möglichkeit, dies zu tun, besteht darin, den Fehler mit a zu ignorieren declare continue handler:

delimiter ;;
create procedure foo ()
begin
    declare continue handler for 1060 begin end;
    alter table atable add subscriber_surname varchar(64);
end;;
call foo();;

Ich denke, es ist so ordentlicher als mit einer existsUnterabfrage. Vor allem, wenn Sie viele Spalten hinzufügen müssen und das Skript mehrmals ausführen möchten.

Weitere Informationen zu Continue-Handlern finden Sie unter http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html


Liebe es! Daran hätte ich nie gedacht. Ich werde mit Sicherheit auf diese Art und Weise umsteigen.
Johnny Kauffman

FEHLER 1060 (42S21): Doppelter Spaltenname 'neuer Spaltenname'
Jake

Das ist wirklich geschickt!
ATOzTOA

6

Ich verwende MySQL 5.5.19.

Ich mag Skripte, die Sie ohne Fehler ausführen und erneut ausführen können, insbesondere wenn Warnungen zu verweilen scheinen und später wieder angezeigt werden, während ich Skripte ausführe, die keine Fehler / Warnungen enthalten. Was das Hinzufügen von Feldern angeht, habe ich mir eine Prozedur geschrieben, um die Eingabe etwas zu vereinfachen:

-- add fields to template table to support ignoring extra data 
-- at the top/bottom of every page
CALL addFieldIfNotExists ('template', 'firstPageHeaderEndY', 'INT NOT NULL DEFAULT 0');
CALL addFieldIfNotExists ('template', 'pageHeaderEndY', 'INT NOT NULL DEFAULT 0');
CALL addFieldIfNotExists ('template', 'pageFooterBeginY', 'INT NOT NULL DEFAULT 792');

Der Code zum Erstellen der Prozedur addFieldIfNotExists lautet wie folgt:

DELIMITER $$

DROP PROCEDURE IF EXISTS addFieldIfNotExists 
$$

DROP FUNCTION IF EXISTS isFieldExisting 
$$

CREATE FUNCTION isFieldExisting (table_name_IN VARCHAR(100), field_name_IN VARCHAR(100)) 
RETURNS INT
RETURN (
    SELECT COUNT(COLUMN_NAME) 
    FROM INFORMATION_SCHEMA.columns 
    WHERE TABLE_SCHEMA = DATABASE() 
    AND TABLE_NAME = table_name_IN 
    AND COLUMN_NAME = field_name_IN
)
$$

CREATE PROCEDURE addFieldIfNotExists (
    IN table_name_IN VARCHAR(100)
    , IN field_name_IN VARCHAR(100)
    , IN field_definition_IN VARCHAR(100)
)
BEGIN

    -- http://javajon.blogspot.com/2012/10/mysql-alter-table-add-column-if-not.html

    SET @isFieldThere = isFieldExisting(table_name_IN, field_name_IN);
    IF (@isFieldThere = 0) THEN

        SET @ddl = CONCAT('ALTER TABLE ', table_name_IN);
        SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ;
        SET @ddl = CONCAT(@ddl, ' ', field_name_IN);
        SET @ddl = CONCAT(@ddl, ' ', field_definition_IN);

        PREPARE stmt FROM @ddl;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

    END IF;

END;
$$

Ich habe keine Prozedur geschrieben, um eine Spalte sicher zu ändern, aber ich denke, die obige Prozedur könnte leicht geändert werden, um dies zu tun.


5

Ich habe den Sproc des OP genommen und ihn wiederverwendbar und schemaunabhängig gemacht. Offensichtlich benötigt es immer noch MySQL 5.

DROP PROCEDURE IF EXISTS AddCol;

DELIMITER //

CREATE PROCEDURE AddCol(
    IN param_schema VARCHAR(100),
    IN param_table_name VARCHAR(100),
    IN param_column VARCHAR(100),
    IN param_column_details VARCHAR(100)
) 
BEGIN
    IF NOT EXISTS(
    SELECT NULL FROM information_schema.COLUMNS
    WHERE COLUMN_NAME=param_column AND TABLE_NAME=param_table_name AND table_schema = param_schema
    )
    THEN
        set @paramTable = param_table_name ;
        set @ParamColumn = param_column ;
        set @ParamSchema = param_schema;
        set @ParamColumnDetails = param_column_details;
        /* Create the full statement to execute */
        set @StatementToExecute = concat('ALTER TABLE `',@ParamSchema,'`.`',@paramTable,'` ADD COLUMN `',@ParamColumn,'` ',@ParamColumnDetails);
        /* Prepare and execute the statement that was built */
        prepare DynamicStatement from @StatementToExecute ;
        execute DynamicStatement ;
        /* Cleanup the prepared statement */
        deallocate prepare DynamicStatement ;

    END IF;
END //

DELIMITER ;

Das funktioniert gut für mich. Die einzige Änderung, die ich vornehmen musste, war das Entfernen der hinteren Anführungszeichen (`) im Aufruf von concat. Sie können den Code auch vereinfachen, indem Sie die Variablen @paramTable, @ParamColumn, @ParamSchema und @ParamColumnDetails entfernen und die Parameter direkt verwenden.
Hrabinowitz

1

Ich habe gerade das Skript für gespeicherte Prozeduren ausprobiert. Das Problem scheinen die 'Markierungen um die Begrenzer zu sein. Die MySQL-Dokumente zeigen, dass Trennzeichen keine einfachen Anführungszeichen benötigen.

Also du möchtest:

delimiter //

Anstatt:

delimiter '//'

Funktioniert bei mir :)


@ Andy Du hast den Punkt komplett verpasst. Dieser Kommentar zeigt auf, wo das OP seinen Fehler gemacht hat. Das OP war bereits da, wenn nicht für die einfachen Anführungszeichen.
RichardTheKiwi

1

Wenn Sie dies in einem Skript ausführen, möchten Sie anschließend die folgende Zeile hinzufügen, damit es erneut ausgeführt werden kann. Andernfalls wird ein bereits vorhandener Prozedurfehler angezeigt.

drop procedure foo;

1

Der beste Weg, um die Spalte in PHP> PDO hinzuzufügen:

$Add = $dbh->prepare("ALTER TABLE `YourCurrentTable` ADD `YourNewColumnName` INT NOT NULL");
$Add->execute();

Hinweis: Die Spalte in der Tabelle ist nicht wiederholbar. Das bedeutet, dass wir nicht die Existenz einer Spalte überprüfen müssen. Um das Problem zu lösen, überprüfen wir den obigen Code:

Zum Beispiel, wenn es funktioniert, Alarm 1, wenn nicht 0, was bedeutet, dass die Spalte existiert! :) :)


1

Überprüfen Sie, ob eine Spalte in der gU vorhanden ist oder nicht (100%).

{
    if(isset($_POST['Add']))
    {
        $ColumnExist = $dbh->prepare("SELECT * FROM ColumnChecker where column_name='$insert_column_name' LIMIT 1");
        $ColumnExist ->execute();
        $ColumnName = $ColumnExist->fetch(2);
        $Display_Column_Name = $ColumnName['column_name'];

        if($Display_Column_Name == $insert_column_name)
        {
            echo "$Display_Column_Name already exist";
        } //*****************************
        else 
        {
            $InsertColumn = $dbh->prepare("insert into ColumnChecker ( column_name ) values ('$insert_column_name')");
            $InsertColumn->execute();

            if($InsertColumn)
            {
                $Add = $dbh->prepare("ALTER TABLE `$Table` ADD `$insert_column_name` $insert_column_type($insert_column_Length) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ");
                $Add->execute();

                if($Add)
                {
                    echo 'Table has been updated';  
                }
                else 
                {
                    echo 'Sorry! Try again...'; 
                }
            }   
        }
    }
}#Add Column into Table :)

1

Das Verfahren von Jake https://stackoverflow.com/a/6476091/6751901 ist eine sehr einfache und gute Lösung zum Hinzufügen neuer Spalten, jedoch mit einer zusätzlichen Zeile:

DROP PROCEDURE IF EXISTS foo;;

Sie können dort später neue Spalten hinzufügen, und es wird auch beim nächsten Mal funktionieren:

delimiter ;;
DROP PROCEDURE IF EXISTS foo;;
create procedure foo ()
begin
    declare continue handler for 1060 begin end;
    alter table atable add subscriber_surname varchar(64);
    alter table atable add subscriber_address varchar(254);
end;;
call foo();;

0
$smpt = $pdo->prepare("SHOW fields FROM __TABLE__NAME__");
$smpt->execute();
$res = $smpt->fetchAll(PDO::FETCH_ASSOC);
//print_r($res);

Suchen Sie dann in $ res nach Zyklus nach dem Schlüssel Ihrer Spalte Smth wie folgt:

    if($field['Field'] == '_my_col_'){
       return true;
    }
+

**Below code is good for checking column existing in the WordPress tables:**
public static function is_table_col_exists($table, $col)
    {
        global $wpdb;
        $fields = $wpdb->get_results("SHOW fields FROM {$table}", ARRAY_A);
        foreach ($fields as $field)
        {
            if ($field['Field'] == $col)
            {
                return TRUE;
            }
        }

        return FALSE;
    }

1
Dies kann ein wenig effizienter gemacht werden SHOW fields FROM __TABLE__NAME__ where field='_my_col_'; und dann überprüfen, ob die Ergebnismenge nicht leer ist
Eugen Mayer

0

Nachfolgend finden Sie die in MySQL gespeicherte Prozedur zum Hinzufügen von Spalten in verschiedenen Tabellen in verschiedenen Datenbanken, wenn die Spalte in einer oder mehreren Datenbanktabellen mit folgenden Vorteilen nicht vorhanden ist

  • Es können mehrere Spalten hinzugefügt werden, um mehrere Tabellen in verschiedenen Datenbanken zu ändern
  • Es werden drei MySQL-Befehle ausgeführt, z. B. DROP, CREATE, CALL For Procedure
  • DATABASE Name sollte gemäß USE geändert werden, da sonst Probleme bei mehreren Daten auftreten können

DROP PROCEDURE  IF EXISTS `AlterTables`;
DELIMITER $$
CREATE PROCEDURE `AlterTables`() 
BEGIN
    DECLARE table1_column1_count INT;
    DECLARE table2_column2_count INT;
    SET table1_column1_count = (  SELECT COUNT(*) 
                    FROM INFORMATION_SCHEMA.COLUMNS
                    WHERE   TABLE_SCHEMA = 'DATABASE_NAME' AND
			    TABLE_NAME = 'TABLE_NAME1' AND 
                            COLUMN_NAME = 'TABLE_NAME1_COLUMN1');
    SET table2_column2_count = (  SELECT COUNT(*) 
                    FROM INFORMATION_SCHEMA.COLUMNS
                    WHERE   TABLE_SCHEMA = 'DATABASE_NAME' AND
			    TABLE_NAME = 'TABLE_NAME2' AND 
                            COLUMN_NAME = 'TABLE_NAME2_COLUMN2');
    IF table1_column1_count = 0 THEN
        ALTER TABLE `TABLE_NAME1`ADD `TABLE_NAME1_COLUMN1` text COLLATE 'latin1_swedish_ci' NULL AFTER `TABLE_NAME1_COLUMN3`,COMMENT='COMMENT HERE';
    END IF;
    IF table2_column2_count = 0 THEN
        ALTER TABLE `TABLE_NAME2` ADD `TABLE_NAME2_COLUMN2` VARCHAR( 100 ) NULL DEFAULT NULL COMMENT 'COMMENT HERE';
    END IF;
END $$
DELIMITER ;
call AlterTables();


-1
ALTER TABLE `subscriber_surname` ADD  IF NOT EXISTS  `#__comm_subscribers`.`subscriber_surname`;

ALTER TABLE `#__comm_subscribers` MODIFY `subscriber_surname` varchar(64) NOT NULL default '';

4
Es wäre großartig, wenn Sie Ihrer Lösung eine Beschreibung hinzufügen könnten, damit wir (die Benutzer) die Vorteile dieser Lösung trotz der anderen verstehen könnten. Dies ist eine Verbesserung für diese und zukünftige Antworten.
Luís Cruz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.