webentwicklung-frage-antwort-db.com.de

ALTER TABLE ADD COLUMN, WENN NICHT in SQLite vorhanden

Vor kurzem mussten wir einigen unserer vorhandenen SQLite-Datenbanktabellen Spalten hinzufügen. Dies kann mit ALTER TABLE ADD COLUMN erfolgen. Wenn die Tabelle bereits geändert wurde, möchten wir sie natürlich in Ruhe lassen. Leider unterstützt SQLite keine IF NOT EXISTS - Klausel für ALTER TABLE.

Unsere derzeitige Problemumgehung besteht darin, die ALTER TABLE-Anweisung auszuführen und alle "doppelten Spaltennamen" -Fehler zu ignorieren, genau wie this Python Beispiel (aber in C++).

Unsere übliche Vorgehensweise zum Einrichten von Datenbankschemata besteht jedoch darin, ein .sql-Skript mit den Anweisungen CREATE TABLE IF NOT EXISTS Und CREATE INDEX IF NOT EXISTS Zu verwenden, das mit sqlite3_exec Oder sqlite3 Befehlszeilenprogramm. Wir können ALTER TABLE Nicht in diese Skriptdateien einfügen, da, wenn diese Anweisung fehlschlägt, alles danach nicht ausgeführt wird.

Ich möchte die Tabellendefinitionen an einem Ort haben und nicht zwischen .sql- und .cpp-Dateien aufteilen. Gibt es eine Möglichkeit, eine Problemumgehung für ALTER TABLE ADD COLUMN IF NOT EXISTS In SQLite SQL zu schreiben?

75
dan04

Ich habe eine zu 99% reine SQL-Methode. Die Idee ist, Ihr Schema zu versionieren. Sie können dies auf zwei Arten tun:

  • Verwenden Sie den Pragma-Befehl 'user_version' ( PRAGMA user_version ), um eine inkrementelle Nummer für Ihre Datenbankschemaversion zu speichern.

  • Speichern Sie Ihre Versionsnummer in Ihrer eigenen Tabelle.

Auf diese Weise kann die Software beim Start das Datenbankschema überprüfen und bei Bedarf Ihr ALTER TABLE abfragen und dann die gespeicherte Version erhöhen. Dies ist bei weitem besser als der Versuch, verschiedene Updates "blind" durchzuführen, insbesondere wenn Ihre Datenbank im Laufe der Jahre einige Male wächst und sich ändert.

50
MPelletier

Eine Problemumgehung besteht darin, nur die Spalten zu erstellen und die Ausnahme/den Fehler abzufangen, die/der auftritt, wenn die Spalte bereits vorhanden ist. Fügen Sie beim Hinzufügen mehrerer Spalten diese in separaten ALTER TABLE-Anweisungen hinzu, damit ein Duplikat die anderen nicht daran hindert, erstellt zu werden.

Mit sqlite-net haben wir so etwas gemacht. Es ist nicht perfekt, da wir doppelte SQLite-Fehler nicht von anderen SQLite-Fehlern unterscheiden können.

Dictionary<string, string> columnNameToAddColumnSql = new Dictionary<string, string>
{
    {
        "Column1",
        "ALTER TABLE MyTable ADD COLUMN Column1 INTEGER"
    },
    {
        "Column2",
        "ALTER TABLE MyTable ADD COLUMN Column2 TEXT"
    }
};

foreach (var pair in columnNameToAddColumnSql)
{
    string columnName = pair.Key;
    string sql = pair.Value;

    try
    {
        this.DB.ExecuteNonQuery(sql);
    }
    catch (System.Data.SQLite.SQLiteException e)
    {
        _log.Warn(e, string.Format("Failed to create column [{0}]. Most likely it already exists, which is fine.", columnName));
    }
}
28
angularsen

SQLite unterstützt auch eine Pragma-Anweisung namens "table_info", die eine Zeile pro Spalte in einer Tabelle mit dem Namen der Spalte (und anderen Informationen zu der Spalte) zurückgibt. Sie können dies in einer Abfrage verwenden, um nach der fehlenden Spalte zu suchen und die Tabelle zu ändern, falls diese nicht vorhanden ist.

PRAGMA table_info(foo_table_name)

http://www.sqlite.org/pragma.html#pragma_table_info

25
Robert Hawkey

Wenn Sie dies in einer DB-Upgrade-Anweisung tun, besteht die einfachste Möglichkeit darin, die geworfene Ausnahme abzufangen, wenn Sie versuchen, ein Feld hinzuzufügen, das möglicherweise bereits vorhanden ist.

try {
   db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null");
} catch (SQLiteException ex) {
   Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage());
}
19
user7896780

ist eine Methode von PRAGMA table_info (table_name), werden alle Informationen der Tabelle zurückgegeben.

Hier ist die Implementierung, wie es für die Check-Spalte verwendet wird oder nicht.

    public boolean isColumnExists (String table, String column) {
         boolean isExists = false
         Cursor cursor;
         try {           
            cursor = db.rawQuery("PRAGMA table_info("+ table +")", null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    if (column.equalsIgnoreCase(name)) {
                        isExists = true;
                        break;
                    }
                }
            }

         } finally {
            if (cursor != null && !cursor.isClose()) 
               cursor.close();
         }
         return isExists;
    }

Sie können diese Abfrage auch verwenden, ohne eine Schleife zu verwenden.

cursor = db.rawQuery("PRAGMA table_info("+ table +") where name = " + column, null);
11
Krunal Shah

Ich habe die obige Antwort in C # /. Net übernommen und sie für Qt/C++ umgeschrieben, nicht zu sehr geändert, aber ich wollte sie hier für jeden hinterlassen, der in Zukunft nach einer C++ 'ish'-Antwort sucht.

    bool MainWindow::isColumnExisting(QString &table, QString &columnName){

    QSqlQuery q;

    try {
        if(q.exec("PRAGMA table_info("+ table +")"))
            while (q.next()) {
                QString name = q.value("name").toString();     
                if (columnName.toLower() == name.toLower())
                    return true;
            }

    } catch(exception){
        return false;
    }
    return false;
}
0
Kevin B Burns

Falls Sie dieses Problem in Flex/Adobe Air haben und sich zuerst hier befinden, habe ich eine Lösung gefunden und sie auf einer verwandten Frage veröffentlicht: ADD COLUMN to sqlite db IF NICHT EXISTIERT - flex/air sqlite?

Mein Kommentar hier: https://stackoverflow.com/a/24928437/2678219

0
stevesweets